#ifndef WIN32

#include "../wmodules.h"
#include "wm_vuln_detector_db.h"
#include "wm_vuln_detector_evr.h"
#include <os_net/os_net.h>
#include "wazuh_db/wdb.h"

#ifdef WAZUH_UNIT_TESTING
// Remove STATIC qualifier from tests
#define STATIC
#else
#define STATIC static
#endif

STATIC char *CPE_VER_TAG = "cpe:2.3:";

STATIC char * wm_vuldet_decode_cpe_term(char *term);
STATIC int wm_vuldet_extract_agent_cpes(agent_software *agent, sqlite3 *db);
STATIC int wm_vuldet_update_agent_cpes(agent_software *agent, sqlite3 *db);
STATIC void wm_vuldet_free_search_terms(vu_search_terms *s_terms);
STATIC vu_search_terms * wm_vuldet_extract_search_terms(char *vendor, char *product, char *version, char *arch);
STATIC int wm_vuldet_insert_nvd_metadata(sqlite3 *db, int year, char *size, char *zip_size, char *g_size, char *sha256, char *last_mod, int cve_count, char alternative);
STATIC int wm_vuldet_parse_nvd_configuration(cJSON *configuration, nvd_vulnerability *data);
STATIC int wm_vuldet_parse_nvd_configuration_node(cJSON *config, const char *cve, nvd_configuration **data);
STATIC int wm_vuldet_parse_nvd_impact(cJSON *impact, nvd_vulnerability *data);
STATIC int wm_vuldet_parse_nvd_cve(cJSON *node, nvd_vulnerability *data);
STATIC int wm_vuldet_insert_nvd_cve_metric_cvss(sqlite3 *db, cv_scoring_system *nvd_data, int node_id);
STATIC int wm_vuldet_insert_nvd_cve_configuration(sqlite3 *db, nvd_configuration *nvd_data, int node_id, int parent);
STATIC int wm_vuldet_insert_nvd_cve_references(sqlite3 *db, nvd_references *nvd_data, char *cve, int node_id);
STATIC int wm_vuldet_insert_nvd_cve_match(sqlite3 *db, nvd_conf_cpe_match *nvd_data, int conf_id);
STATIC int wm_vuldet_insert_nvd_cve_cpe(sqlite3 *db, cpe *cpe_data);
STATIC void wm_vuldet_free_nvd_references(nvd_references *data);
STATIC void wm_vuldet_free_nvd_conf_cpe_match(nvd_conf_cpe_match *data);
STATIC void wm_vuldet_free_nvd_configuration(nvd_configuration *data);
STATIC void wm_vuldet_free_nvd_report(vu_nvd_report *report);
STATIC void wm_vuldet_free_nvd_report_list(vu_nvd_report *nvd_report_list);
STATIC int wm_vuldet_find_nvd_vulnerabilities(sqlite3 *db, agent_software *agent, wm_vuldet_flags *flags, vu_nvd_report **nvd_report_list);
STATIC int wm_vuldet_get_vuln_nvd_cpe(sqlite3 *db, agent_software *agent, wm_vuldet_flags *flags, cpe *agent_cpe, char *raw_product, char *raw_version, char *raw_arch, vu_nvd_report **nvd_report_list);
STATIC vu_nvd_report *wm_vuldet_check_nvd_vulnerability(sqlite3 *db, agent_software *agent, wm_vuldet_flags *flags, cpe *agent_cpe, int cve_id, int conf_id, int vulnerable, char *operator, int parent, char *uri_version, char *v_start_inc, char *v_start_exc, char *v_end_inc, char *v_end_exc);
STATIC int wm_vuldet_report_nvd_vulnerabilities(sqlite3 *db, vu_nvd_report **nvd_report, agent_software *agent);

/**
 * @brief Clean a package's version by removing the epoch and revision (if available).
 * @param version Raw version to be cleaned.
 * @return version already cleaned.
 */
STATIC char *wm_vuldet_clean_version(const char *version);

/**
 * @brief Filter vulnerabilities by vendor and target_sw.
 * @param feed Agent OS feed.
 * @param vendor The vendor of the vulnerability to be analyzed.
 * @param target_sw The target SW of the vulnerability to be analyzed.
 * @return 1 skip this vulnerability, 0 otherwise.
 */
STATIC int vw_vuldet_filter_vulnerabilities(vu_feed feed, const char *vendor, const char *target_sw);

/**
 * @brief Check if a generic NVD package is vulnerable.
 * @param dbCVE Database with NVD information.
 * @param pkg_name Name of package to check.
 * @param pkg_source Source of package to check.
 * @param pkg_version Version of package to check.
 * @param pkg_arch Architecture of package to check.
 * @param cve_table Vulnerability data hash table.
 * @param name_type Package information: source or name.
 * @param vuln_count Vulnerability counter.
 * @return 1 vulnerable, 0 no vulnerable, -1 otherwise.
 */
STATIC int wm_vuldet_check_generic_package(sqlite3 *dbCVE, vu_feed feed, char *pkg_name, char *pkg_source, char *pkg_version, char *pkg_arch, char *pkg_vendor, OSHash *cve_table, int8_t name_type, int *vuln_count);

/**
 * @brief Check if a specific NVD package is vulnerable.
 * @param dbCVE Database with NVD information.
 * @param pkg_name Name of package to check.
 * @param pkg_source Source of package to check.
 * @param pkg_version Version of package to check.
 * @param pkg_arch Architecture of package to check.
 * @param cve_table Vulnerability data hash table.
 * @param name_type Package information: source or name.
 * @param vuln_count Vulnerability counter.
 * @return 1 vulnerable, 0 no vulnerable, -1 otherwise.
 */
STATIC int wm_vuldet_check_specific_package(sqlite3 *dbCVE, vu_feed feed, char *pkg_name, char *pkg_source, char *pkg_version, char *pkg_arch, char *pkg_vendor, OSHash *cve_table, int8_t name_type, int *vuln_count);

/**
 * @brief Fill an array with the children's IDs from a package.
 * @param dbCVE Database with NVD information.
 * @param configuration_id ID from table NVD_CVE_CONFIGURATION.
 * @param package_id ID from table NVD_CVE_MATCH.
 * @param children Array to be filled.
 * @return 0 on success, -1 otherwise.
 */
STATIC int wm_vuldet_get_children(sqlite3 * dbCVE, int configuration_id, int package_id, int *children);

/**
 * @brief Fill an array with the siblings' IDs from a package.
 * @param dbCVE Database with NVD information.
 * @param parent Parent package ID from table NVD_CVE_CONFIGURATION.
 * @param configuration_id ID from table NVD_CVE_CONFIGURATION.
 * @param siblings Array to be filled.
 * @return 0 on success, -1 otherwise.
 */
STATIC int wm_vuldet_get_siblings(sqlite3 * dbCVE, int parent, int configuration_id, int *siblings);

STATIC void wm_vuldet_build_nvd_condition(vu_nvd_report *report, char **condition, char *is_hotfix);
STATIC int wm_vuldet_check_nvd_logical(sqlite3 *db, char *agent_id, vu_nvd_report *rp, int conf_id, int parent);
STATIC int wm_vuldet_add_extra_package(vu_nvd_report *rp, char *vendor, char *product);
STATIC int wm_vuldet_generate_agent_cpes_with_terms(sqlite3 *db, agent_software *agent);
STATIC int wm_vuldet_generate_agent_cpes_with_dic(sqlite3 *db, agent_software *agent);
STATIC int wm_vuldet_get_dic_matches(sqlite3 *db, char *agent_id, sqlite3_stmt **stmt);
STATIC int wm_vuldet_add_dic_cpe(sqlite3 *db, int *cpe_index, cpe_list **cpe_list, cpe *o_app, cpe *g_app);
STATIC int wm_vuldet_get_translate_terms(sqlite3 *db, int cpeh_id, int action, int vendor_pos, int product_pos, int version_pos, int sw_ed_pos, int msu_pos, cpe *g_app);
STATIC int wm_vuldet_get_correlation_id(sqlite3 *db, int cpeh_id, const char *type, char *search_term, char **match);
STATIC int wm_vuldet_dic_check_rep_vendor(sqlite3 *db, agent_software *agent, int *min_cpe_index, cpe_list **node_list);
STATIC int wm_vuldet_get_translate_term(sqlite3 *db, int cpeh_id, cpe_tags type, int pos, cpe *g_app);
STATIC vu_logic wm_vuldet_check_hotfix(sqlite3 *db, agent_software *agent, wm_vuldet_flags *flags, int cve_id, cpe *s_cpe, char **necessary_patch);
STATIC void wm_vuldet_expand_msu_variables(cpe *g_app);
STATIC char *wm_vuldet_extract_product_field(char *pattern, char *product);
STATIC void wm_vuldet_solve_cond_translate_term(cpe *g_app);
STATIC int wm_vuldet_eval_cond_term(cpe *g_app, translation_cond *cond);
STATIC void wm_vuldet_adapt_targethw(char **target_hw);
STATIC void wm_vuldet_fit_nvd_matches(const char *cve, nvd_conf_cpe_match **cpe_match_node);
STATIC void wm_vuldet_free_nvd_conf_cpe_match_node(nvd_conf_cpe_match *data);
STATIC void wm_vuldet_adapt_os_cpe(cpe *ag_cpe);
STATIC int wm_vuln_valid_os_cpe(vu_feed dist, cpe *ag_cpe, char *c_version);
STATIC char *wm_vuldet_generate_msu_request(cpe *s_cpe, vu_feed dist_ver);

static const char *PR_UBUNTU = "ubuntu_linux";
static const char *PR_DEBIAN = "debian_linux";
static const char *PR_REDHAT = "enterprise_linux";
static const char *PR_KERNEL = "linux_kernel";
static const char *V_UBUNTU = "canonical";
static const char *V_DEBIAN = "debian";
static const char *V_REDHAT = "redhat";
static const char *V_KERNEL = "linux";

// Common tags
const char *vu_cpe_tags[] = {
    "vendor",
    "product",
    "version",
    "sw_edition",
    "update",
    "target_hw",
    "msu_name"
};

// Target SW whitelist for Linux
const char *vu_linux_target_sw_wl[] = {
    "*",
    "linux",
    "linux_kernel",
    "mupdf",
    "gpl_ghostscript"
};

// Target SW whitelist for macOS
const char *vu_mac_target_sw_wl[] = {
    "*",
    "mac",
    "mac_os",
    "mac_os_x",
    "macos"
};

int wm_vuldet_nvd_cpe_parser(char *path, wm_vuldet_db *parsed_vulnerabilities) {
    FILE *fp;
    char buffer[OS_MAXSTR + 1];
    static char *cpe_header = "-item name=\"cpe:2.";
    size_t cpe_header_size = strlen(cpe_header);
    char *cpe_found;
    cpe_list *node_list;

    if (fp = fopen(path, "r"), !fp) {
        return OS_INVALID;
    }

    os_calloc(1, sizeof(cpe_list), node_list);
    while (fgets(buffer, OS_MAXSTR, fp)) {
        if (cpe_found = strstr(buffer, cpe_header), cpe_found) {
            char *cpe_end;
            cpe_found += cpe_header_size;
            if (cpe_found = strchr(cpe_found, ':'), cpe_found) {
                if (cpe_end = wstr_chr(++cpe_found, '"'), cpe_end) {
                    *cpe_end = '\0';
                    wm_vuldet_add_cpe(NULL, cpe_found, node_list, 0);
                }
            }
        }
    }

    fclose(fp);
    parsed_vulnerabilities->nvd_cpes = node_list;

    return 0;
}

int wm_vuldet_add_cpe(cpe *ncpe, char *cpe_raw, cpe_list *node_list, int index) {
    cpe_node *item;

    os_calloc(1, sizeof(cpe_node), item);

    if (!ncpe) {
        if (item->node = wm_vuldet_decode_cpe(cpe_raw), !item->node) {
            os_free(item);
            return OS_INVALID;
        }
    } else {
        item->node = wm_vuldet_cpe_cpy(ncpe);
    }

    item->node->id = index;

    if(node_list->first == NULL) {
        node_list->first = item;
    } else {
        item->prev = node_list->last;
        node_list->last->next = item;
    }

    node_list->last = item;
    return 0;
}

int wm_vuldet_insert_cpe_db(sqlite3 *db, cpe_list *node_list, char overwrite) {
    sqlite3_stmt *stmt = NULL;
    cpe_node *node_it;
    cpe *node;
    int result;

    if (overwrite) {
        if (wm_vuldet_remove_table(db, "CPE_INDEX")) {
            return OS_INVALID;
        }
    }

    for (node_it = node_list->first; node_it; node_it = node_it->next) {
        char *normalized_name = NULL;
        node = node_it->node;

        if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_CPE], -1, &stmt, NULL) != SQLITE_OK) {
            goto error;
        }

        if (node->id) {
            sqlite3_bind_int(stmt, 1, node->id);
        } else {
            sqlite3_bind_null(stmt, 1);
        }

        sqlite3_bind_int(stmt, 2, node->pos);
        sqlite3_bind_text(stmt, 3, node->part, -1, NULL);
        sqlite3_bind_text(stmt, 4, node->vendor, -1, NULL);

        normalized_name = w_tolower_str(node->product);
        sqlite3_bind_text(stmt, 5, normalized_name, -1, NULL);

        sqlite3_bind_text(stmt, 6, node->version, -1, NULL);
        sqlite3_bind_text(stmt, 7, node->update, -1, NULL);
        sqlite3_bind_text(stmt, 8, node->edition, -1, NULL);
        sqlite3_bind_text(stmt, 9, node->language, -1, NULL);
        sqlite3_bind_text(stmt, 10, node->sw_edition, -1, NULL);
        sqlite3_bind_text(stmt, 11, node->target_sw, -1, NULL);
        sqlite3_bind_text(stmt, 12, node->target_hw, -1, NULL);
        sqlite3_bind_text(stmt, 13, node->other, -1, NULL);
        sqlite3_bind_text(stmt, 14, node->msu_name, -1, NULL);

        if (result = wm_vuldet_step(stmt), result != SQLITE_DONE) {
            os_free(normalized_name);
            goto error;
        }

        wdb_finalize(stmt);
        os_free(normalized_name);
    }

    return 0;
error:
    mterror(WM_VULNDETECTOR_LOGTAG, VU_SQL_ERROR, sqlite3_errmsg(db));
    wdb_finalize(stmt);
    return OS_INVALID;
}

char *wm_vuldet_decode_cpe_term(char *term) {
    char *decoded_term;

    term = os_strip_char(term, '\\');
    decoded_term = wstr_replace(term, "&amp;", "&");
    free(term);
    term = wstr_replace(decoded_term, "&quot;", "\"");
    free(decoded_term);

    return term;
}

cpe *wm_vuldet_decode_cpe(char *raw_cpe) {
    cpe *decoded_cpe;
    char *cpeuri;
    char *cpe_str;
    char *cpe_strnext;
    int success = 0;

    os_calloc(1, sizeof(cpe), decoded_cpe);
    os_strdup(raw_cpe, cpeuri);
    str_lowercase(cpeuri);
    //{part}:{vendor}:{product}:{version}:{update}:{edition}:{language}:{sw_edition}:{target_sw}:{target_hw}:{other}

    if (cpe_str = wstr_chr(cpeuri, ':'), !cpe_str) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CPE_PARSING_ERROR, vu_cpe_tags[CPE_VENDOR], raw_cpe);
        goto end;
    }
    *cpe_str++ = '\0';
    os_strdup(cpeuri, decoded_cpe->part);

    if (cpe_strnext = wstr_chr(cpe_str, ':'), !cpe_strnext) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CPE_PARSING_ERROR, vu_cpe_tags[CPE_PRODUCT], raw_cpe);
        goto end;
    }
    *cpe_strnext++ = '\0';
    os_strdup(cpe_str, decoded_cpe->vendor);
    cpe_str = cpe_strnext;

    if (cpe_strnext = wstr_chr(cpe_str, ':'), !cpe_strnext) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CPE_PARSING_ERROR, vu_cpe_tags[CPE_VERSION], raw_cpe);
        goto end;
    }
    *cpe_strnext++ = '\0';
    decoded_cpe->product = wm_vuldet_decode_cpe_term(cpe_str);
    cpe_str = cpe_strnext;

    if (cpe_strnext = wstr_chr(cpe_str, ':'), !cpe_strnext) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CPE_PARSING_ERROR, vu_cpe_tags[CPE_UPDATE], raw_cpe);
        goto end;
    }
    *cpe_strnext++ = '\0';
    decoded_cpe->version = wm_vuldet_decode_cpe_term(cpe_str);
    cpe_str = cpe_strnext;

    if (cpe_strnext = wstr_chr(cpe_str, ':'), !cpe_strnext) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CPE_PARSING_ERROR, "edition", raw_cpe);
        goto end;
    }
    *cpe_strnext++ = '\0';
    os_strdup(cpe_str, decoded_cpe->update);
    cpe_str = cpe_strnext;

    if (cpe_strnext = wstr_chr(cpe_str, ':'), !cpe_strnext) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CPE_PARSING_ERROR, "language", raw_cpe);
        goto end;
    }
    *cpe_strnext++ = '\0';
    os_strdup(cpe_str, decoded_cpe->edition);
    cpe_str = cpe_strnext;

    if (cpe_strnext = wstr_chr(cpe_str, ':'), !cpe_strnext) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CPE_PARSING_ERROR, "target_sw", raw_cpe);
        goto end;
    }
    *cpe_strnext++ = '\0';
    os_strdup(cpe_str, decoded_cpe->language);
    cpe_str = cpe_strnext;

    if (cpe_strnext = wstr_chr(cpe_str, ':'), !cpe_strnext) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CPE_PARSING_ERROR, "sw_edition", raw_cpe);
        goto end; //For cpe version 2.2
    }
    *cpe_strnext++ = '\0';
    os_strdup(cpe_str, decoded_cpe->sw_edition);
    cpe_str = cpe_strnext;

    if (cpe_strnext = wstr_chr(cpe_str, ':'), !cpe_strnext) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CPE_PARSING_ERROR, "target_hw", raw_cpe);
        goto end;
    }
    *cpe_strnext++ = '\0';
    os_strdup(cpe_str, decoded_cpe->target_sw);
    cpe_str = cpe_strnext;

    if (cpe_strnext = wstr_chr(cpe_str, ':'), !cpe_strnext) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_CPE_PARSING_ERROR, "other", raw_cpe);
        goto end;
    }
    *cpe_strnext++ = '\0';
    os_strdup(cpe_str, decoded_cpe->target_hw);
    os_strdup(cpe_strnext, decoded_cpe->other);

    decoded_cpe->raw = cpeuri;
    success = 1;
end:
    if (!success) {
        os_free(cpeuri);
        wm_vuldet_free_cpe(decoded_cpe);
        return NULL;
    }
    return decoded_cpe;
}

void wm_vuldet_free_cpe(cpe *node) {
    int i;

    // CPE fields
    free(node->part);
    free(node->vendor);
    free(node->product);
    free(node->version);
    free(node->update);
    free(node->edition);
    free(node->language);
    free(node->sw_edition);
    free(node->target_sw);
    free(node->target_hw);
    free(node->other);
    // Extra fields
    free(node->msu_name);
    free(node->raw);
    // Multi-fields
    if (node->cm_product) {
        for (i = 0; (node->cm_product + i)->translation; i++) {
            free((void *) (node->cm_product + i)->translation);
            free((void *) (node->cm_product + i)->field);
            free((void *) (node->cm_product + i)->condition);
        }

        free(node->cm_product);
    }


    free(node);
}

void wm_vuldet_free_multi_cpe(cpe *node) {
    while (node) {
        cpe *next = node->next;
        wm_vuldet_free_cpe(node);
        node = next;
    }
}

int wm_vuldet_get_min_cpe_index(sqlite3 *db, int *min_index) {
    sqlite3_stmt *stmt = NULL;
    int success = 0;
    char close_db = 0;

    if (!db) {
        if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) {
            wm_vuldet_sql_error(db, stmt);
            return OS_INVALID;
        }
        close_db = 1;
    }

    if (wm_vuldet_prepare(db, vu_queries[VU_MIN_CPEINDEX], -1, &stmt, NULL) != SQLITE_OK) {
        wm_vuldet_sql_error(db, stmt);
        return OS_INVALID;
    }
    if (wm_vuldet_step(stmt) == SQLITE_ROW) {
        if (*min_index = sqlite3_column_int(stmt, 0), *min_index >= 0) {
            *min_index = -1;
        } else {
            (*min_index)--;
        }
    } else {
        goto end;
    }

    success = 1;
end:
    wdb_finalize(stmt);
    if (close_db) {
        sqlite3_close_v2(db);
    }
    return !success;
}

int wm_vuldet_extract_agent_cpes(agent_software *agent, sqlite3 *db) {
    sqlite3_stmt *stmt = NULL;
    vu_search_terms *s_terms = NULL;
    vu_search_terms *pkg_terms = NULL;
    int pkg_count, cpes_count;
    char *generated_cpe;
    int min_cpe_index;
    int success = 0;
    vu_search_terms *f_pkg_terms;
    cpe_list *node_list = NULL;

    if (wm_vuldet_prepare(db, vu_queries[VU_GET_PACK_WITHOUT_CPE], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, agent->agent_id, -1, NULL);

    // Get the search terms for each package
    while (wm_vuldet_step(stmt) == SQLITE_ROW) {
        char *vendor = (char *) sqlite3_column_text(stmt, 0);
        char *product = (char *) sqlite3_column_text(stmt, 1);
        char *version = (char *) sqlite3_column_text(stmt, 2);
        char *arch = (char *) sqlite3_column_text(stmt, 3);

        if (pkg_terms = wm_vuldet_extract_search_terms(vendor, product, version, arch), !pkg_terms) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_GEN_SEARCH_TERMS_ERROR, vendor, product, version, arch, atoi(agent->agent_id));
            continue;
        }

        if (s_terms) {
            s_terms->next = pkg_terms;
            pkg_terms->prev = s_terms;
        }
        s_terms = pkg_terms;
    }
    wdb_finalize(stmt);

    // Use the search terms to find a valid CPE for each package
    for (pkg_count = 0, cpes_count = 0, pkg_terms = s_terms; pkg_terms;) {
        int v_index, p_index;
        pkg_count++;
        for (v_index = 0; pkg_terms->vendor_terms[v_index]; v_index++) {
            char *part = NULL;
            for (p_index = 0; pkg_terms->product_terms[p_index]; p_index++) {
                if (wm_vuldet_prepare(db, vu_queries[VU_SEARCH_AGENT_CPE], -1, &stmt, NULL) != SQLITE_OK) {
                    return wm_vuldet_sql_error(db, stmt);
                }

                sqlite3_bind_text(stmt, 1, pkg_terms->vendor_terms[v_index], -1, NULL);
                sqlite3_bind_text(stmt, 2, pkg_terms->product_terms[p_index], -1, NULL);

                if (wm_vuldet_step(stmt) == SQLITE_ROW) {
                    cpe software_cpe;

                    memset(&software_cpe, 0, sizeof(cpe));
                    part = (char *) sqlite3_column_text(stmt, 0);

                    software_cpe.part = part;
                    software_cpe.vendor = pkg_terms->vendor_terms[v_index];
                    software_cpe.product = pkg_terms->product_terms[p_index];
                    software_cpe.version = pkg_terms->o_version;
                    software_cpe.target_hw = pkg_terms->o_arch;

                    generated_cpe = wm_vuldet_cpe_str(&software_cpe);
                    str_lowercase(generated_cpe);
                    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_CPE_GENERATED, pkg_terms->o_vendor, pkg_terms->o_product, generated_cpe);
                    pkg_terms->generated_cpe = generated_cpe;
                    cpes_count++;

                    wdb_finalize(stmt);
                    break;
                }

                wdb_finalize(stmt);
            }
            if (part) {
                break;
            }
        }
        pkg_terms = pkg_terms->prev;
    }

    if (wm_vuldet_get_min_cpe_index(db, &min_cpe_index)) {
        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_CPE_INDEX_GET_ERROR);
        goto end;
    }

    os_calloc(1, sizeof(cpe_list), node_list);
    // Link the CPEs with its packages
    for (pkg_terms = s_terms; pkg_terms;) {
        if (pkg_terms->generated_cpe) {
            if (wm_vuldet_prepare(db, vu_queries[VU_UPDATE_AGENT_CPE], -1, &stmt, NULL) != SQLITE_OK) {
                goto end;
            }

            sqlite3_bind_int(stmt, 1, min_cpe_index);
            sqlite3_bind_int(stmt, 2, atoi(agent->agent_id));
            sqlite3_bind_text(stmt, 3,pkg_terms->o_vendor, -1, NULL);
            sqlite3_bind_text(stmt, 4, pkg_terms->o_product, -1, NULL);
            sqlite3_bind_text(stmt, 5, pkg_terms->o_version, -1, NULL);
            sqlite3_bind_text(stmt, 6, pkg_terms->o_arch, -1, NULL);

            if (wm_vuldet_step(stmt) != SQLITE_DONE) {
                wm_vuldet_sql_error(db, stmt);
                db = NULL;
                stmt = NULL;
                goto end;
            }
            wdb_finalize(stmt);

            if (wm_vuldet_add_cpe(NULL, pkg_terms->generated_cpe, node_list, min_cpe_index)) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_PARSING_ERROR, pkg_terms->generated_cpe, atoi(agent->agent_id));
                goto end;
            }

            min_cpe_index--;
        }

        f_pkg_terms = pkg_terms;
        pkg_terms = pkg_terms->prev;
        wm_vuldet_free_search_terms(f_pkg_terms);
        free(f_pkg_terms);
    }

    stmt = NULL;
    if (wm_vuldet_insert_cpe_db(db, node_list, 0)) {
        goto end;
    }

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_GEN_CPE_COUNT, atoi(agent->agent_id), cpes_count, pkg_count);

    s_terms = NULL;
    success = 1;
end:
    if (node_list) {
        wm_vuldet_free_cpe_list(node_list);
        free(node_list);
    }

    wdb_finalize(stmt);

    if (pkg_terms) {
        while (pkg_terms) {
            f_pkg_terms = pkg_terms;
            pkg_terms = pkg_terms->prev;
            wm_vuldet_free_search_terms(f_pkg_terms);
            free(f_pkg_terms);
        }
    }

    return success ? 0 : OS_INVALID;
}


int wm_vuldet_update_agent_cpes(agent_software *agent, sqlite3 *db) {
    char buffer[OS_SIZE_6144] = "";
    sqlite3_stmt *stmt = NULL;
    int success = 0;

    if (wm_vuldet_prepare(db, vu_queries[VU_GET_AGENT_CPES], -1, &stmt, NULL) != SQLITE_OK) {
        wm_vuldet_sql_error(db, stmt);
        db = NULL;
        goto end;
    }

    sqlite3_bind_text(stmt, 1, agent->agent_id, -1, NULL);

    // Get the search terms for each package
    while (wm_vuldet_step(stmt) == SQLITE_ROW) {
        const char *cpe_part = (const char *) sqlite3_column_text(stmt, 0);
        const char *cpe_vendor = (const char *) sqlite3_column_text(stmt, 1);
        const char *cpe_product = (const char *) sqlite3_column_text(stmt, 2);
        const char *cpe_version = (const char *) sqlite3_column_text(stmt, 3);
        const char *cpe_updatev = (const char *) sqlite3_column_text(stmt, 4);
        const char *cpe_edition = (const char *) sqlite3_column_text(stmt, 5);
        const char *cpe_language = (const char *) sqlite3_column_text(stmt, 6);
        const char *cpe_sw_edition = (const char *) sqlite3_column_text(stmt, 7);
        const char *cpe_target_sw = (const char *) sqlite3_column_text(stmt, 8);
        const char *cpe_target_hw = (const char *) sqlite3_column_text(stmt, 9);
        const char *cpe_other = (const char *) sqlite3_column_text(stmt, 10);
        const char *cpe_msu_name = (const char *) sqlite3_column_text(stmt, 11);
        const char *vendor = (const char *) sqlite3_column_text(stmt, 12);
        const char *package = (const char *) sqlite3_column_text(stmt, 13);
        const char *version = (const char *) sqlite3_column_text(stmt, 14);
        const char *arch = (const char *) sqlite3_column_text(stmt, 15);

        // If the vendor or package string contains single quotation characters
        // sqlite3_snprintf will escape them with double single quotes to avoid breaking the SQL query
        sqlite3_snprintf(OS_SIZE_6144, buffer, vu_queries[VU_SYSC_UPDATE_CPE],
            agent->agent_id, cpe_part, cpe_vendor, cpe_product, cpe_version, cpe_updatev,
            cpe_edition, cpe_language, cpe_sw_edition, cpe_target_sw, cpe_target_hw, cpe_other,
            cpe_msu_name, vendor, package, version, arch);
        if (wm_vuldet_wdb_request(buffer, OS_SIZE_6144)) {
            goto end;
        }

        // Check the wazuh-db answer
        if (!wm_vuldet_wdb_valid_answ(buffer)) {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_INV_WAZUHDB_RES, buffer);
            goto end;
        }
    }

    success = 1;
end:
    wdb_finalize(stmt);

    if (!success) {
        if (db) {
            sqlite3_close_v2(db);
        }
        return OS_INVALID;
    }
    return 0;
}

int wm_vuldet_generate_agent_cpes_with_terms(sqlite3 *db, agent_software *agent) {

    if (wm_vuldet_extract_agent_cpes(agent, db)) {
        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_AGENT_CPE_EXT_ERROR, atoi(agent->agent_id));
        return OS_INVALID;
    }

    if (wm_vuldet_update_agent_cpes(agent, db)) {
        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_AGENT_CPE_UPD_ERROR, atoi(agent->agent_id));
        return OS_INVALID;
    }

    return 0;
}

vu_search_terms * wm_vuldet_extract_search_terms(char *vendor, char *product, char *version, char *arch) {
    vu_search_terms *s_terms = NULL;
    char *vendor_found = NULL;
    int vendor_words = w_word_counter(vendor);
    int product_words = w_word_counter(product);
    int fit_product_words = 0;
    int i;
    char *vendor_cpy;
    char *product_cpy;

    os_calloc(1, sizeof(vu_search_terms), s_terms);

    os_strdup(vendor, s_terms->o_vendor);
    os_strdup(vendor, vendor_cpy);
    os_strdup(product, s_terms->o_product);
    os_strdup(product, product_cpy);
    os_strdup(version, s_terms->o_version);
    os_strdup(arch, s_terms->o_arch);

    str_lowercase(vendor_cpy);
    str_lowercase(product_cpy);

    os_calloc(2, sizeof(char *), s_terms->vendor_terms);
    os_calloc(3, sizeof(char *), s_terms->product_terms);
    os_strdup(vendor_cpy, *s_terms->vendor_terms);
    os_strdup(product_cpy, *s_terms->product_terms);
    wchr_replace(*s_terms->product_terms, ' ', '_');

    if (vendor_words != 3) wstr_split(vendor_cpy, " ", NULL, 3, &s_terms->vendor_terms);
    if (vendor_words != 2) wstr_split(vendor_cpy, " ", NULL, 2, &s_terms->vendor_terms);
    if (vendor_words != 1) wstr_split(vendor_cpy, " ", NULL, 1, &s_terms->vendor_terms);

    // If the vendor appears inside the product, it will be taken into account separately
    for (i = 0; s_terms->vendor_terms[i]; i++) {
        if (vendor_found = strstr(product_cpy, s_terms->vendor_terms[i]), vendor_found) {
            vendor_found += strlen(s_terms->vendor_terms[i]);
            vendor_found += *vendor_found == ' ' ? 1 : 0;
            if (*vendor_found != '\0') {
                os_strdup(vendor_found, s_terms->product_terms[1]);
                wchr_replace(s_terms->product_terms[1], ' ', '_');
                fit_product_words = w_word_counter(vendor_found);
            } else {
                vendor_found = NULL;
            }

            break;
        }
    }

    if (vendor_found && fit_product_words != 4) wstr_split(vendor_found, " ", "_", 4, &s_terms->product_terms);
    if (product_words != 4) wstr_split(product_cpy, " ", "_", 4, &s_terms->product_terms);

    if (vendor_found && fit_product_words != 3) wstr_split(vendor_found, " ", "_", 3, &s_terms->product_terms);
    if (product_words != 3 ) wstr_split(product_cpy, " ", "_", 3, &s_terms->product_terms);

    if (vendor_found && fit_product_words != 2) wstr_split(vendor_found, " ", "_", 2, &s_terms->product_terms);
    if (product_words != 2) wstr_split(product_cpy, " ", "_", 2, &s_terms->product_terms);

    if (vendor_found && fit_product_words != 1) {
        wstr_split(vendor_found, " ", "_", 1, &s_terms->product_terms);
        wstr_split(s_terms->vendor_terms[i], " ", "_", 1, &s_terms->product_terms);
    } else if (product_words != 1) {
        wstr_split(product_cpy, " ", "_", 1, &s_terms->product_terms);
    }

    for (i = 0; s_terms->vendor_terms[i]; i++) {
        wchr_replace(s_terms->vendor_terms[i], ' ', '_');
    }

    free(vendor_cpy);
    free(product_cpy);

    return s_terms;
}

void wm_vuldet_free_search_terms(vu_search_terms *s_terms) {
    int i;
    os_free(s_terms->o_vendor);
    os_free(s_terms->o_product);
    os_free(s_terms->o_version);
    os_free(s_terms->o_arch);
    os_free(s_terms->generated_cpe)

    if (s_terms->vendor_terms) {
        for (i = 0; s_terms->vendor_terms[i]; i++) {
            os_free(s_terms->vendor_terms[i]);
        }
        os_free(s_terms->vendor_terms);
    }

    if (s_terms->product_terms) {
        for (i = 0; s_terms->product_terms[i]; i++) {
            os_free(s_terms->product_terms[i]);
        }
        os_free(s_terms->product_terms);
    }

    if (s_terms->version_terms) {
        for (i = 0; s_terms->version_terms[i]; i++) {
            os_free(s_terms->version_terms[i]);
        }
        os_free(s_terms->version_terms);
    }

    if (s_terms->arch_terms) {
        for (i = 0; s_terms->arch_terms[i]; i++) {
            os_free(s_terms->arch_terms[i]);
        }
        os_free(s_terms->arch_terms);
    }
}

int wm_vuldet_fetch_nvd_cpe(const long timeout, char *repo) {
    if (*repo == '\0') {
        snprintf(repo, OS_SIZE_256, NVD_CPE_REPO);
    }

    if (wstr_end(repo, ".gz")) {
        if (wurl_request_gz(repo, VU_TEMP_FILE, NULL, NULL, timeout, NULL)) {
            return OS_INVALID;
        }
    } else {
        if (wurl_request(repo, VU_TEMP_FILE, NULL, NULL, timeout)) {
            return OS_INVALID;
        }
    }

    return 0;
}

int wm_vuldet_fetch_nvd_cve(update_node *update) {
    int attempt;
    int retval = VU_INV_FEED;
    char buffer[OS_MAXSTR + 1];
    FILE *fp = NULL;
    FILE *fp_out = NULL;
    char *found;
    sqlite3_stmt *stmt = NULL;
    sqlite3 *db = NULL;
    static char *feed_last_mod = "lastModifiedDate:";
    char *last_mod = NULL;
    int res_url_request;

    if (update->multi_url) {
        char tag[10 + 1];
        char *repo;

        snprintf(tag, 10, "%d", update->update_it);
        repo = wstr_replace(update->multi_url, MULTI_URL_TAG, tag);

        for (attempt = 0; attempt < NVD_REPO_MAX_ATTEMPTS; attempt++) {
            if (wurl_request_uncompress_bz2_gz(repo, VU_FIT_TEMP_FILE, NULL, NULL, update->timeout, NULL)) {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_API_REQ_INV, repo, attempt * DOWNLOAD_SLEEP_FACTOR);
                sleep(attempt * DOWNLOAD_SLEEP_FACTOR);
                continue;
            }
            break;
        }

        if (attempt == NVD_REPO_MAX_ATTEMPTS) {
            mtwarn(WM_VULNDETECTOR_LOGTAG, VU_API_REQ_INV, repo, NVD_REPO_MAX_ATTEMPTS);
            free(repo);
            goto end;
        }
        free(repo);
    } else {
        char repo[OS_SIZE_2048 + 1];
        char str_it[21];

        // Check the metadata feed
        snprintf(repo, OS_SIZE_2048, NVD_CVE_REPO_META, update->update_it);
        for (attempt = 0; attempt < NVD_REPO_MAX_ATTEMPTS; attempt++) {
            if (wurl_request(repo, VU_TEMP_FILE, NULL, NULL, update->timeout)) {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_API_REQ_INV, repo, attempt * DOWNLOAD_SLEEP_FACTOR);
                sleep(attempt * DOWNLOAD_SLEEP_FACTOR);
                continue;
            }
            break;
        }
        if (attempt == NVD_REPO_MAX_ATTEMPTS) {
            mtwarn(WM_VULNDETECTOR_LOGTAG, VU_API_REQ_INV, repo, NVD_REPO_MAX_ATTEMPTS);
            goto end;
        }

        if (fp = fopen(VU_TEMP_FILE, "r"), !fp) {
            goto end;
        }

        if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READWRITE, NULL)) {
            wm_vuldet_sql_error(db, stmt);
            db = NULL;
            return VU_INV_FEED;
        }

        while (fgets(buffer, OS_MAXSTR, fp)) {
            if (found = strstr(buffer, feed_last_mod), found) {
                last_mod = found + strlen(feed_last_mod);

                if (wm_vuldet_prepare(db, vu_queries[VU_GET_NVD_LASTMOD], -1, &stmt, NULL) != SQLITE_OK) {
                    wm_vuldet_sql_error(db, stmt);
                    db = NULL;
                    return VU_INV_FEED;
                }
                sqlite3_bind_int(stmt, 1, update->update_it);

                if (wm_vuldet_step(stmt) == SQLITE_ROW) {
                    found = (char *)sqlite3_column_text(stmt, 0);

                    // Check if our feed is outdated
                    if (!strncmp(last_mod, found, strlen(found))) {
                        wdb_finalize(stmt);
                        retval = VU_NOT_NEED_UPDATE;
                        goto end;
                    }
                }
                snprintf(str_it, 20, " (%d)", update->update_it);
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_DB_TIMESTAMP_FEED, update->dist_ext, str_it);
                wdb_finalize(stmt);
                break;
            }
        }

        if (fp_out = fopen(VU_TEMP_METADATA_FILE, "w"), !fp_out) {
            goto end;
        }

        fseek(fp, 0, SEEK_SET);

        while (fgets(buffer, OS_MAXSTR, fp)) {
            fwrite(buffer, 1, strlen(buffer), fp_out);
        }

        w_fclose(fp);
        w_fclose(fp_out);

        // At this point we know that we must update the feed for this year
        snprintf(repo, OS_SIZE_2048, NVD_CVE_REPO, update->update_it);
        for (attempt = 0; attempt < NVD_REPO_MAX_ATTEMPTS; attempt++) {
            res_url_request = wurl_request_uncompress_bz2_gz(repo, VU_FIT_TEMP_FILE, NULL, NULL, update->timeout, NULL);
            if (res_url_request) {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_API_REQ_INV, repo, attempt * DOWNLOAD_SLEEP_FACTOR);
                sleep(attempt * DOWNLOAD_SLEEP_FACTOR);
                continue;
            }
            break;
        }

        if (attempt == NVD_REPO_MAX_ATTEMPTS) {
            mtwarn(WM_VULNDETECTOR_LOGTAG, VU_API_REQ_INV, repo, NVD_REPO_MAX_ATTEMPTS);
            goto end;
        }
    }

    retval = 0;
end:
    if (db) {
        sqlite3_close_v2(db);
    }
    if (fp) {
        fclose(fp);
        if (remove(VU_TEMP_FILE) < 0 && errno != ENOENT) {
            mtdebug2(WM_VULNDETECTOR_LOGTAG, "remove(%s): %s", VU_TEMP_FILE, strerror(errno));
        }
    }
    return retval;
}

int wm_vuldet_insert_nvd_metadata(sqlite3 *db, int year, char *size, char *zip_size, char *g_size, char *sha256, char *last_mod, int cve_count, char alternative) {
    sqlite3_stmt *stmt;

    if (wm_vuldet_prepare(db, vu_queries[VU_REP_NVD_METADATA], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_int(stmt, 1, year);
    sqlite3_bind_text(stmt, 2, size, -1, NULL);
    sqlite3_bind_text(stmt, 3, zip_size, -1, NULL);
    sqlite3_bind_text(stmt, 4, g_size, -1, NULL);
    sqlite3_bind_text(stmt, 5, sha256, -1, NULL);
    sqlite3_bind_text(stmt, 6, last_mod, -1, NULL);
    sqlite3_bind_int(stmt, 7, cve_count);
    sqlite3_bind_int(stmt, 8, alternative);

    if (wm_vuldet_step(stmt) != SQLITE_DONE) {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);

    return 0;
}

int wm_vuldet_clean_nvd_metadata(sqlite3 *db, int year) {
    sqlite3_stmt *stmt = NULL;

    if (wm_vuldet_prepare(db, vu_queries[VU_REMOVE_NVD_METADATA], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_int(stmt, 1, year);

    if (wm_vuldet_step(stmt) != SQLITE_DONE) {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);

    return 0;
}

int wm_vuldet_json_nvd_parser(char *json_feed, wm_vuldet_db *parsed_vulnerabilities) {
    cJSON *cve_content;
    nvd_vulnerability *nvd_it = NULL;
    nvd_vulnerability *nvd_first = NULL;
    static char *JSON_CVE = "cve";
    static char *JSON_CONFIGURATIONS = "configurations";
    static char *JSON_IMPACT = "impact";
    static char *JSON_PUBLISHED = "publishedDate";
    static char *JSON_LAST_MOD = "lastModifiedDate";
    static char *JSON_DATA_TYPE = "data_type";
    static char *JSON_DATA_FORMAT = "data_format";
    static char *JSON_DATA_VERSION = "data_version";

    const char * cve;
    const char * next_cve;
    const char * match_cve = " {\r\n    \"cve\" : {\r\n";

    for (cve = strstr(json_feed, match_cve); cve != NULL; cve = strstr(next_cve, match_cve)) {

        cJSON * cve_list;

        if (cve_list = cJSON_ParseWithOpts(cve, &next_cve, 0), !cve_list) {
            wm_vuldet_free_nvd_list(nvd_first);
            return OS_INVALID;
        }

        if (nvd_it) {
            os_calloc(1, sizeof(nvd_vulnerability), nvd_it->next);
            nvd_it = nvd_it->next;
        } else {
            os_calloc(1, sizeof(nvd_vulnerability), nvd_it);
            nvd_first = nvd_it;
        }
        for (cve_content = cve_list->child; json_tagged_obj(cve_content); cve_content = cve_content->next) {
            if (!strcmp(cve_content->string, JSON_CVE)) {
                wm_vuldet_parse_nvd_cve(cve_content, nvd_it);
            } else if (!strcmp(cve_content->string, JSON_CONFIGURATIONS)) {
                wm_vuldet_parse_nvd_configuration(cve_content, nvd_it);
            } else if (!strcmp(cve_content->string, JSON_IMPACT)) {
                wm_vuldet_parse_nvd_impact(cve_content, nvd_it);
            } else if (!strcmp(cve_content->string, JSON_PUBLISHED)) {
                w_strdup(cve_content->valuestring, nvd_it->published);
            } else if (!strcmp(cve_content->string, JSON_LAST_MOD)) {
                w_strdup(cve_content->valuestring, nvd_it->last_modified);
            } else if (strcmp(cve_content->string, JSON_DATA_TYPE) &&
                        strcmp(cve_content->string, JSON_DATA_FORMAT) &&
                        strcmp(cve_content->string, JSON_DATA_VERSION)) {
                mtwarn(WM_VULNDETECTOR_LOGTAG, VU_UNKNOWN_NVD_CVE_TAG, cve_content->string);
            } else {
                mtwarn(WM_VULNDETECTOR_LOGTAG, VU_UNKNOWN_NVD_TAG, cve_content->string);
            }
        }

        cJSON_Delete(cve_list);
    }

    if (nvd_it) {
        if (parsed_vulnerabilities->nvd_vulnerabilities) {
            nvd_it->next = parsed_vulnerabilities->nvd_vulnerabilities;
        }
        parsed_vulnerabilities->nvd_vulnerabilities = nvd_first;
    }

    return 0;
}

int wm_vuldet_parse_nvd_cve(cJSON *node, nvd_vulnerability *data) {
    cJSON *cve_data;
    cJSON *cve_data_it;
    static char *JSON_DESCRIPTION_DATA = "description_data";
    static char *JSON_LANG = "lang";
    static char *JSON_VALUE = "value";
    static char *JSON_EN_LANG = "en";
    static char *JSON_CVE_DATA_META = "CVE_data_meta";
    static char *JSON_CVE_DATA_META_ID = "ID";
    static char *JSON_CVE_DATA_META_ASSIGNER = "ASSIGNER";
    static char *JSON_PROBLEM_TYPE = "problemtype";
    static char *JSON_PROBLEM_TYPE_DATA = "problemtype_data";
    static char *JSON_REFERENCES = "references";
    static char *JSON_REFERENCE_DATA = "reference_data";
    static char *JSON_URL = "url";
    static char *JSON_REFERENCE_SOURCE = "refsource";
    static char *JSON_DESCRIPTION = "description";

    for (cve_data = node->child; json_tagged_obj(cve_data); cve_data = cve_data->next) {
        if (!strcmp(cve_data->string, JSON_CVE_DATA_META)) {
            for (cve_data_it = cve_data->child; json_tagged_obj(cve_data_it); cve_data_it = cve_data_it->next) {
                if (!strcmp(cve_data_it->string, JSON_CVE_DATA_META_ID)) {
                    w_strdup(cve_data_it->valuestring, data->id);
                } else if (!strcmp(cve_data_it->string, JSON_CVE_DATA_META_ASSIGNER)) {
                    w_strdup(cve_data_it->valuestring, data->assigner);
                }
            }
        } else if (!strcmp(cve_data->string, JSON_PROBLEM_TYPE)) {
            if ((cve_data_it = cve_data->child) && !strcmp(cve_data_it->string, JSON_PROBLEM_TYPE_DATA)) {
                if ((cve_data_it = cve_data_it->child ? cve_data_it->child->child : NULL) && !strcmp(cve_data_it->string, JSON_DESCRIPTION)) {
                    for (cve_data_it = cve_data_it->child ? cve_data_it->child->child : NULL; json_tagged_obj(cve_data_it); cve_data_it = cve_data_it->next) {
                        if (!strcmp(cve_data_it->string, JSON_VALUE)) {
                            w_strdup(cve_data_it->valuestring, data->cwe);
                            break;
                        }
                    }
                }
            }
        } else if (!strcmp(cve_data->string, JSON_REFERENCES)) {
            if ((cve_data_it = cve_data->child) && !strcmp(cve_data_it->string, JSON_REFERENCE_DATA)) {
                nvd_references *ref_node = NULL;
                for (cve_data_it = cve_data_it->child; cve_data_it; cve_data_it = cve_data_it->next) {
                    cJSON *reference_it;
                    if (ref_node) {
                        os_calloc(1, sizeof(nvd_references), ref_node->next);
                        ref_node = ref_node->next;
                    } else {
                        os_calloc(1, sizeof(nvd_references), data->references);
                        ref_node = data->references;
                    }
                    for (reference_it = cve_data_it->child; json_tagged_obj(reference_it); reference_it = reference_it->next) {
                        if (!strcmp(reference_it->string, JSON_URL)) {
                            w_strdup(reference_it->valuestring, ref_node->url);
                        } else if (!strcmp(reference_it->string, JSON_REFERENCE_SOURCE)) {
                            w_strdup(reference_it->valuestring, ref_node->refsource);
                        }
                    }
                }
            }
        } else if (!strcmp(cve_data->string, JSON_DESCRIPTION)) {
            if ((cve_data_it = cve_data->child) && !strcmp(cve_data_it->string, JSON_DESCRIPTION_DATA)) {
                char valid_desc;
                cJSON *desc_it;
                for (; cve_data_it; cve_data_it = cve_data_it->next) {
                    valid_desc = 0;
                    for (desc_it = cve_data_it->child ? cve_data_it->child->child : NULL; json_tagged_obj(desc_it); desc_it = desc_it->next) {
                        if (!strcmp(desc_it->string, JSON_VALUE)) {
                            w_strdup(desc_it->valuestring, data->description);
                        } else if (!strcmp(desc_it->string, JSON_LANG) && desc_it->valuestring && !strcmp(desc_it->valuestring, JSON_EN_LANG)) {
                            valid_desc = 1;
                        }
                    }
                    if (valid_desc) {
                        break;
                    } else {
                        os_free(data->description);
                    }
                }
            }
        }
    }

    return 0;
}

int wm_vuldet_parse_nvd_configuration(cJSON *nvd_item, nvd_vulnerability *data) {
    static char *JSON_CVE_DATA_VERSION = "CVE_data_version";
    static char *JSON_NODES = "nodes";

    for (nvd_item = nvd_item->child; json_tagged_obj(nvd_item); nvd_item = nvd_item->next) {
        if (!strcmp(nvd_item->string, JSON_CVE_DATA_VERSION)) {
            w_strdup(nvd_item->valuestring, data->version);
        }
        else if (!strcmp(nvd_item->string, JSON_NODES)) {
            wm_vuldet_parse_nvd_configuration_node(nvd_item, data->id, &data->configuration);
            break;
        }
    }

    return 0;
}

int wm_vuldet_parse_nvd_configuration_node(cJSON *config, const char *cve, nvd_configuration **data) {
    cJSON *config_it;
    cJSON *cpe_match_item;
    cJSON *cpe_match_element;
    nvd_configuration *data_config_it = NULL;
    nvd_conf_cpe_match *cpe_match_node = NULL;
    static char *JSON_OPERATOR = "operator";
    static char *JSON_CHILDREN = "children";
    static char *JSON_CPE_MATCHES = "cpe_match";
    static char *JSON_VULNERABLE = "vulnerable";
    static char *JSON_CPE23URI = "cpe23Uri";
    static char *JSON_START_INC = "versionStartIncluding";
    static char *JSON_END_INC = "versionEndIncluding";
    static char *JSON_START_EXC = "versionStartExcluding";
    static char *JSON_END_EXC = "versionEndExcluding";

    for (config = config->child; config; config = config->next) {
        if (data_config_it) {
            os_calloc(1, sizeof(nvd_configuration), data_config_it->next);
            data_config_it = data_config_it->next;
        } else {
            os_calloc(1, sizeof(nvd_configuration), *data);
            data_config_it = *data;
        }
        cpe_match_node = NULL;

        for (config_it = config->child; json_tagged_obj(config_it); config_it = config_it->next) {
            if (!strcmp(config_it->string, JSON_OPERATOR)) {
                w_strdup(config_it->valuestring, data_config_it->operator);
            } else if (!strcmp(config_it->string, JSON_CHILDREN)) {
                wm_vuldet_parse_nvd_configuration_node(config_it, cve, &data_config_it->children);
            } else if (!strcmp(config_it->string, JSON_CPE_MATCHES)) {
                for (cpe_match_item = config_it->child; cpe_match_item; cpe_match_item = cpe_match_item->next) {
                    if (cpe_match_node) {
                        os_calloc(1, sizeof(nvd_conf_cpe_match), cpe_match_node->next);
                        cpe_match_node = cpe_match_node->next;
                    } else {
                        os_calloc(1, sizeof(nvd_conf_cpe_match), data_config_it->cpe_matches);
                        cpe_match_node = data_config_it->cpe_matches;
                    }
                    for(cpe_match_element = cpe_match_item->child; json_tagged_obj(cpe_match_element); cpe_match_element = cpe_match_element->next) {
                        if (!strcmp(cpe_match_element->string, JSON_VULNERABLE)) {
                            cpe_match_node->vulnerable = cpe_match_element->valueint;
                        } else if (!strcmp(cpe_match_element->string, JSON_CPE23URI) && cpe_match_element->valuestring) {
                            os_strdup(cpe_match_element->valuestring, cpe_match_node->cpe_uri23);
                            cpe_match_node->cpe_node = wm_vuldet_decode_cpe(cpe_match_node->cpe_uri23 + strlen(CPE_VER_TAG));
                            wm_vuldet_adapt_os_cpe(cpe_match_node->cpe_node);
                        } else if (!strcmp(cpe_match_element->string, JSON_START_INC)) {
                            w_strdup(cpe_match_element->valuestring, cpe_match_node->version_start_including);
                        } else if (!strcmp(cpe_match_element->string, JSON_END_INC)) {
                            w_strdup(cpe_match_element->valuestring, cpe_match_node->version_end_including);
                        } else if (!strcmp(cpe_match_element->string, JSON_START_EXC)) {
                            w_strdup(cpe_match_element->valuestring, cpe_match_node->version_start_excluding);
                        } else if (!strcmp(cpe_match_element->string, JSON_END_EXC)) {
                            w_strdup(cpe_match_element->valuestring, cpe_match_node->version_end_excluding);
                        }
                    }
                }
            }
        }

        wm_vuldet_fit_nvd_matches(cve, &data_config_it->cpe_matches);
    }

    return 0;
}

int wm_vuldet_parse_nvd_impact(cJSON *nvd_item, nvd_vulnerability *data) {
    cJSON *impact_element;
    cJSON *cvss_data;
    cv_scoring_system * scoring = NULL;
    static char *cvss3_vector_tag = "CVSS";
    static char *JSON_V20 = "2.";
    static char *JSON_V30 = "3.";
    static char *JSON_VECT_STR = "vectorString";
    static char *JSON_BASE_SCORE = "baseScore";
    static char *JSON_EXP_SCORE = "exploitabilityScore";
    static char *JSON_IMP_SCORE = "impactScore";

    for (nvd_item = nvd_item->child; nvd_item; nvd_item = nvd_item->next) {
        for (impact_element = nvd_item->child; json_tagged_obj(impact_element); impact_element = impact_element->next) {
            char score_added = 0;
            os_calloc(1, sizeof(cv_scoring_system), scoring);
            for (cvss_data = impact_element->child; json_tagged_obj(cvss_data); cvss_data = cvss_data->next) {
                if (!strcmp(cvss_data->string, vu_cpe_tags[CPE_VERSION]) && cvss_data->valuestring) {
                    if (strstr(cvss_data->valuestring, JSON_V20)) {
                        score_added = 1;
                        data->cvss2 = scoring;
                    } else if (strstr(cvss_data->valuestring, JSON_V30)) {
                        score_added = 1;
                        data->cvss3 = scoring;
                    }
                    os_strdup(cvss_data->valuestring, scoring->version);
                } else if (!strcmp(cvss_data->string, JSON_VECT_STR) && cvss_data->valuestring) {
                    if (strstr(cvss_data->valuestring, cvss3_vector_tag)) {
                        w_strdup(strstr(cvss_data->valuestring, "/"), scoring->vector_string);
                    } else {
                        w_strdup(cvss_data->valuestring, scoring->vector_string);
                    }
                } else if (!strcmp(cvss_data->string, JSON_BASE_SCORE)) {
                    scoring->base_score = cvss_data->valuedouble;
                }
            }

            // Release the scoring structure if it is invalid
            if (!score_added) {
                wm_vuldet_free_cv_scoring_system(scoring);
                continue;
            }

            if (!strcmp(impact_element->string, JSON_EXP_SCORE)) {
                scoring->exploitability_score = impact_element->valuedouble;
            } else if (!strcmp(impact_element->string, JSON_IMP_SCORE)) {
                scoring->impact_score = impact_element->valuedouble;
            }
        }
    }

    return 0;
}

int wm_vuldet_insert_nvd_cve(sqlite3 *db, nvd_vulnerability *nvd_data, int year) {
    sqlite3_stmt *stmt = NULL;
    int result;
    int id_nvd_cve = 0;

    if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_NVD_CVE], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_int(stmt, 1, year);
    sqlite3_bind_text(stmt, 2, nvd_data->id, -1, NULL);
    sqlite3_bind_text(stmt, 3, nvd_data->cwe, -1, NULL);
    sqlite3_bind_text(stmt, 4, nvd_data->assigner, -1, NULL);
    sqlite3_bind_text(stmt, 5, nvd_data->description, -1, NULL);
    sqlite3_bind_text(stmt, 6, nvd_data->version, -1, NULL);
    sqlite3_bind_text(stmt, 7, nvd_data->published, -1, NULL);
    sqlite3_bind_text(stmt, 8, nvd_data->last_modified, -1, NULL);

    if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);

    if (wm_vuldet_prepare(db, vu_queries[VU_GET_MAX_NVD_CVE_ID], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    if (result = wm_vuldet_step(stmt), result != SQLITE_ROW) {
        return wm_vuldet_sql_error(db, stmt);
    }

    id_nvd_cve = sqlite3_column_int(stmt, 0);
    wdb_finalize(stmt);

    if (id_nvd_cve > 0) {
        if (wm_vuldet_insert_nvd_cve_references(db, nvd_data->references, nvd_data->id, id_nvd_cve)) {
            return OS_INVALID;
        }
        if (nvd_data->configuration) {
            if (wm_vuldet_insert_nvd_cve_configuration(db, nvd_data->configuration, id_nvd_cve, 0)) {
                return OS_INVALID;
            }
        }
        if (nvd_data->cvss2) {
            if (wm_vuldet_insert_nvd_cve_metric_cvss(db, nvd_data->cvss2, id_nvd_cve)) {
                return OS_INVALID;
            }
        }
        if (nvd_data->cvss3) {
            if (wm_vuldet_insert_nvd_cve_metric_cvss(db, nvd_data->cvss3, id_nvd_cve)) {
                return OS_INVALID;
            }
        }
    } else {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_NVD_ROW_GET_ERROR, "NVD_CVE");
        sqlite3_close_v2(db);
        return OS_INVALID;
    }

    return 0;
}

int wm_vuldet_insert_nvd_cve_references(sqlite3 *db, nvd_references *nvd_data, char *cve, int node_id) {
    nvd_references *node;
    sqlite3_stmt *stmt = NULL;
    bool nvd_url = false;
    int result;

    node = nvd_data;
    while(node && node->url) {
        if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_NVD_REFERENCE], -1, &stmt, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, stmt);
        }
        sqlite3_bind_int(stmt, 1, node_id);
        sqlite3_bind_text(stmt, 2, node->url, -1, NULL);
        sqlite3_bind_text(stmt, 3, node->refsource, -1, NULL);

        if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
            return wm_vuldet_sql_error(db, stmt);
        }

        // Check if there is included a link to the official NVD website
        if (!strncmp(node->url, NVD_CVE_URL, 34)) {
            nvd_url = true;
        }
        wdb_finalize(stmt);
        node = node->next;
    }

    // Insert the reference to the NVD if it was not included
    if (!nvd_url) {
        char * nvd_cve_ref;
        size_t url_length = 35 + strlen(cve);
        os_calloc(url_length, sizeof(char), nvd_cve_ref);
        snprintf(nvd_cve_ref, url_length, "%s%s", NVD_CVE_URL, cve);

        if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_NVD_REFERENCE], -1, &stmt, NULL) != SQLITE_OK) {
            os_free(nvd_cve_ref);
            return wm_vuldet_sql_error(db, stmt);
        }
        sqlite3_bind_int(stmt, 1, node_id);
        sqlite3_bind_text(stmt, 2, nvd_cve_ref, -1, NULL);
        sqlite3_bind_text(stmt, 3, "MISC", -1, NULL);

        if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
            os_free(nvd_cve_ref);
            return wm_vuldet_sql_error(db, stmt);
        }

        os_free(nvd_cve_ref);
        wdb_finalize(stmt);
    }

    return 0;
}

int wm_vuldet_insert_nvd_cve_metric_cvss(sqlite3 *db, cv_scoring_system *nvd_data, int node_id) {
    sqlite3_stmt *stmt = NULL;
    int result;

    if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_NVD_METRIC_CVSS], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_int(stmt, 1, node_id);
    sqlite3_bind_text(stmt, 2, nvd_data->version, -1, NULL);
    sqlite3_bind_text(stmt, 3, nvd_data->vector_string, -1, NULL);
    sqlite3_bind_double(stmt, 4, nvd_data->base_score);
    sqlite3_bind_double(stmt, 5, nvd_data->exploitability_score);
    sqlite3_bind_double(stmt, 6, nvd_data->impact_score);

    if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);

    return 0;
}

int wm_vuldet_insert_nvd_cve_configuration(sqlite3 *db, nvd_configuration *nvd_data, int cve_node_id, int parent) {
    nvd_configuration *node;
    sqlite3_stmt *stmt = NULL;
    int result;
    int conf_id = 0;

    node = nvd_data;
    while(node) {
        if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_NVD_CVE_CONFIGURATION], -1, &stmt, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, stmt);
        }
        sqlite3_bind_int(stmt, 1, cve_node_id);
        sqlite3_bind_int(stmt, 2, parent);
        sqlite3_bind_text(stmt, 3, node->operator, -1, NULL);

        if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
            return wm_vuldet_sql_error(db, stmt);
        }
        wdb_finalize(stmt);

        if (wm_vuldet_prepare(db, vu_queries[VU_GET_MAX_CONFIGURATION_ID], -1, &stmt, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, stmt);
        }

        if (result = wm_vuldet_step(stmt), result != SQLITE_ROW) {
            return wm_vuldet_sql_error(db, stmt);
        }

        conf_id = sqlite3_column_int(stmt, 0);
        wdb_finalize(stmt);

        if (conf_id > 0) {
            if(node->children) {
                if (wm_vuldet_insert_nvd_cve_configuration(db, node->children, cve_node_id, conf_id)) {
                    return OS_INVALID;
                }
            }
            if(node->cpe_matches) {
                if (wm_vuldet_insert_nvd_cve_match(db, node->cpe_matches, conf_id)) {
                    return OS_INVALID;
                }
            }
        } else {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_NVD_ROW_GET_ERROR, "NVD_CVE_CONFIGURATION");
            sqlite3_close_v2(db);
            return OS_INVALID;
        }
        node = node->next;
    }

    return 0;
}

int wm_vuldet_insert_nvd_cve_match(sqlite3 *db, nvd_conf_cpe_match *nvd_data, int conf_id) {
    nvd_conf_cpe_match *node;
    sqlite3_stmt *stmt = NULL;
    int result;
    int cpe_id;

    node = nvd_data;
    while(node) {
        if (cpe_id = wm_vuldet_insert_nvd_cve_cpe(db, node->cpe_node), !cpe_id) {
            return OS_INVALID;
        }

        if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_NVD_CVE_MATCHES], -1, &stmt, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, stmt);
        }
        sqlite3_bind_int(stmt, 1, conf_id);
        sqlite3_bind_int(stmt, 2, cpe_id);
        sqlite3_bind_int(stmt, 3, node->vulnerable);
        sqlite3_bind_text(stmt, 4, node->cpe_uri23, -1, NULL);
        sqlite3_bind_text(stmt, 5, node->version_start_including, -1, NULL);
        sqlite3_bind_text(stmt, 6, node->version_start_excluding, -1, NULL);
        sqlite3_bind_text(stmt, 7, node->version_end_including, -1, NULL);
        sqlite3_bind_text(stmt, 8, node->version_end_excluding, -1, NULL);

        if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
            return wm_vuldet_sql_error(db, stmt);
        }
        wdb_finalize(stmt);
        node = node->next;
    }

    return 0;
}

int wm_vuldet_insert_nvd_cve_cpe(sqlite3 *db, cpe *cpe_data) {
    sqlite3_stmt *stmt = NULL;
    int result;
    unsigned long int cpe_id = 0;

    if (wm_vuldet_prepare(db, vu_queries[VU_GET_MAX_NVD_CPE_ID], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }
    result = wm_vuldet_step(stmt);

    if (result == SQLITE_ROW) {
        if (cpe_id = sqlite3_column_int(stmt, 0), cpe_id > 0) {
            cpe_id++;
        } else {
            cpe_id = 1;
        }
    } else if (result == SQLITE_DONE) {
        cpe_id = 1;
    } else {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);

    if (wm_vuldet_prepare(db, vu_queries[VU_INSERT_NVD_CPE], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }
    sqlite3_bind_int(stmt, 1, cpe_id);
    sqlite3_bind_text(stmt, 2, cpe_data->part, -1, NULL);
    sqlite3_bind_text(stmt, 3, cpe_data->vendor, -1, NULL);
    sqlite3_bind_text(stmt, 4, cpe_data->product, -1, NULL);
    sqlite3_bind_text(stmt, 5, cpe_data->version, -1, NULL);
    sqlite3_bind_text(stmt, 6, cpe_data->update, -1, NULL);
    sqlite3_bind_text(stmt, 7, cpe_data->edition, -1, NULL);
    sqlite3_bind_text(stmt, 8, cpe_data->language, -1, NULL);
    sqlite3_bind_text(stmt, 9, cpe_data->sw_edition, -1, NULL);
    sqlite3_bind_text(stmt, 10, cpe_data->target_sw, -1, NULL);
    sqlite3_bind_text(stmt, 11, cpe_data->target_hw, -1, NULL);
    sqlite3_bind_text(stmt, 12, cpe_data->other, -1, NULL);

    if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);

    if(result == SQLITE_CONSTRAINT) {
        if (wm_vuldet_prepare(db, vu_queries[VU_GET_AN_CPE_ID], -1, &stmt, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, stmt);
        }
        sqlite3_bind_text(stmt, 1, cpe_data->part, -1, NULL);
        sqlite3_bind_text(stmt, 2, cpe_data->vendor, -1, NULL);
        sqlite3_bind_text(stmt, 3, cpe_data->product, -1, NULL);
        sqlite3_bind_text(stmt, 4, cpe_data->version, -1, NULL);
        sqlite3_bind_text(stmt, 5, cpe_data->update, -1, NULL);
        sqlite3_bind_text(stmt, 6, cpe_data->edition, -1, NULL);
        sqlite3_bind_text(stmt, 7, cpe_data->language, -1, NULL);
        sqlite3_bind_text(stmt, 8, cpe_data->sw_edition, -1, NULL);
        sqlite3_bind_text(stmt, 9, cpe_data->target_sw, -1, NULL);
        sqlite3_bind_text(stmt, 10, cpe_data->target_hw, -1, NULL);
        sqlite3_bind_text(stmt, 11, cpe_data->other, -1, NULL);

        if (result = wm_vuldet_step(stmt), result != SQLITE_ROW) {
            return wm_vuldet_sql_error(db, stmt);
        }
        cpe_id = sqlite3_column_int(stmt, 0);
        wdb_finalize(stmt);
    }

    return cpe_id;
}

void wm_vuldet_free_nvd_references(nvd_references *data) {
    nvd_references *node;

    while (data) {
        node = data->next;
        os_free(data->url);
        os_free(data->refsource);
        os_free(data);
        data = node;
    }
}

void wm_vuldet_free_nvd_conf_cpe_match(nvd_conf_cpe_match *data) {
    nvd_conf_cpe_match *node;

    while (data) {
        node = data->next;
        wm_vuldet_free_nvd_conf_cpe_match_node(data);
        data = node;
    }
}

void wm_vuldet_free_nvd_conf_cpe_match_node(nvd_conf_cpe_match *data) {
        os_free(data->cpe_uri23);
        os_free(data->version_start_including);
        os_free(data->version_start_excluding);
        os_free(data->version_end_including);
        os_free(data->version_end_excluding);
        wm_vuldet_free_cpe(data->cpe_node);
        os_free(data);
}

void wm_vuldet_free_nvd_configuration(nvd_configuration *data) {
    nvd_configuration *node;

    while (data) {
        node = data->next;
        wm_vuldet_free_nvd_conf_cpe_match(data->cpe_matches);
        wm_vuldet_free_nvd_configuration(data->children);
        os_free(data->operator);
        os_free(data);
        data = node;
    }
}

void wm_vuldet_free_cv_scoring_system(cv_scoring_system *data) {
    if(data) {
        os_free(data->vector_string);
        os_free(data->version);
        os_free(data);
    }
}

void wm_vuldet_free_nvd_node(nvd_vulnerability *data) {
    os_free(data->id);
    os_free(data->cwe);
    os_free(data->description);
    wm_vuldet_free_nvd_references(data->references);
    wm_vuldet_free_nvd_configuration(data->configuration);
    wm_vuldet_free_cv_scoring_system(data->cvss2);
    wm_vuldet_free_cv_scoring_system(data->cvss3);
    os_free(data->published);
    os_free(data->last_modified);
    os_free(data);
}

void wm_vuldet_free_nvd_list(nvd_vulnerability *nvd_it) {
    while (nvd_it) {
        nvd_vulnerability *r_node = nvd_it;
        nvd_it = nvd_it->next;
        wm_vuldet_free_nvd_node(r_node);
    }
}

int wm_vuldet_linux_nvd_vulnerabilities(sqlite3 *db, agent_software *agent, OSHash *cve_table) {
    sqlite3_stmt *stmt = NULL;
    time_t start_time;
    int *vuln_count;
    char *pkg_source = NULL;
    char *pkg_name = NULL;
    char *pkg_version = NULL;
    char *pkg_source_version = NULL;
    char *pkg_arch = NULL;
    char *pkg_vendor = NULL;

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_START_NVD_AG_AN, atoi(agent->agent_id));

    start_time = time(NULL);

    //Collect packages and versions from sys_programs
    if (wm_vuldet_prepare(db, vu_queries[VU_AGENT_PACKAGE_VERSION], -1, &stmt, NULL) != SQLITE_OK) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_GET_PACKAGES_ERROR, atoi(agent->agent_id));
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, agent->agent_id, -1, NULL);

    os_calloc(1, sizeof(int), vuln_count);

    while(SQLITE_ROW == wm_vuldet_step(stmt)) {

        char *version;
        package_tags type;
        sqlite_strdup((char *)sqlite3_column_text(stmt, PACKAGE_SOURCE), pkg_source);
        sqlite_strdup((char *)sqlite3_column_text(stmt, PACKAGE_NAME), pkg_name);
        sqlite_strdup((char *)sqlite3_column_text(stmt, PACKAGE_VERSION), pkg_version);
        sqlite_strdup((char *)sqlite3_column_text(stmt, PACKAGE_SOURCE_VERSION), pkg_source_version);
        sqlite_strdup((char *)sqlite3_column_text(stmt, PACKAGE_ARCH), pkg_arch);
        sqlite_strdup((char *)sqlite3_column_text(stmt, PACKAGE_VENDOR), pkg_vendor);

        if (!pkg_name || !pkg_version || !pkg_arch) {
            goto clean;
        }

        // If we have a source version, use it to find vulnerabilities.
        if (pkg_source) {
            if (pkg_source_version) {
                version = pkg_source_version;
            } else {
                version = pkg_version;
            }
            type = PACKAGE_SOURCE;
        } else {
            version = pkg_version;
            type = PACKAGE_NAME;
        }

        //Generic matches
        if(wm_vuldet_check_generic_package(db, agent->dist, pkg_name, pkg_source, version, pkg_arch, pkg_vendor, cve_table, type, vuln_count) == OS_INVALID) {
            goto end;
        }

        //Specific matches
        if(wm_vuldet_check_specific_package(db, agent->dist, pkg_name, pkg_source, version, pkg_arch, pkg_vendor, cve_table, type, vuln_count) == OS_INVALID) {
            goto end;
        }

clean:
        os_free(pkg_source);
        os_free(pkg_name);
        os_free(pkg_version);
        os_free(pkg_source_version);
        os_free(pkg_arch);
        os_free(pkg_vendor);
    }

    wdb_finalize(stmt);

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_VULN_NVD_AG_COUNT, *vuln_count, atoi(agent->agent_id));
    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_FUNCTION_TIME, time(NULL) - start_time, "find NVD", atoi(agent->agent_id));

    os_free(vuln_count);

    return 0;

end:
    os_free(pkg_source);
    os_free(pkg_name);
    os_free(pkg_version);
    os_free(pkg_source_version);
    os_free(pkg_arch);
    os_free(pkg_vendor);

    wdb_finalize(stmt);
    os_free(vuln_count);
    return OS_INVALID;
}

int wm_vuldet_win_nvd_vulnerabilities(sqlite3 *db, agent_software *agent, wm_vuldet_flags *flags) {
    vu_nvd_report *nvd_report_list = NULL;
    time_t start_time;
    time_t find_vul_time;
    time_t report_vul_time;

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_START_NVD_AG_AN, atoi(agent->agent_id));

    start_time = time(NULL);

    // Find the vulnerabilities
    if (wm_vuldet_find_nvd_vulnerabilities(db, agent, flags, &nvd_report_list)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_FIND_NVD_ERROR, atoi(agent->agent_id));
        wm_vuldet_free_nvd_report_list(nvd_report_list);
        return OS_INVALID;
    }

    find_vul_time = time(NULL) - start_time;
    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_FUNCTION_TIME, find_vul_time, "find NVD", atoi(agent->agent_id));

    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_START_VUL_AG_SEND, atoi(agent->agent_id));

    // Report the vulnerabilities
    if (wm_vuldet_report_nvd_vulnerabilities(db, &nvd_report_list, agent)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_REPORT_NVD_ERROR, atoi(agent->agent_id));
        wm_vuldet_free_nvd_report_list(nvd_report_list);
        return OS_INVALID;
    }

    report_vul_time = time(NULL) - start_time - find_vul_time;
    mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_FUNCTION_TIME, report_vul_time, "report", atoi(agent->agent_id));

    return 0;
}

int wm_vuldet_find_nvd_vulnerabilities(sqlite3 *db, agent_software *agent, wm_vuldet_flags *flags, vu_nvd_report **nvd_report_list) {
    sqlite3_stmt *stmt = NULL;
    int result;
    char *raw_product;
    char *raw_version;
    char *raw_arch;
    cpe *stored_cpe;

    if (wm_vuldet_prepare(db, vu_queries[VU_GET_DICT_CPE], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }
    sqlite3_bind_text(stmt, 1, agent->agent_id, -1, NULL);

    while(result = wm_vuldet_step(stmt), result != SQLITE_DONE) {
        switch (result) {
        case SQLITE_ROW:
            stored_cpe = wm_vuldet_generate_cpe((const char *)sqlite3_column_text(stmt, 0),
                                                    (const char *)sqlite3_column_text(stmt, 1),
                                                    (const char *)sqlite3_column_text(stmt, 2),
                                                    (const char *)sqlite3_column_text(stmt, 3),
                                                    (const char *)sqlite3_column_text(stmt, 4),
                                                    (const char *)sqlite3_column_text(stmt, 5),
                                                    (const char *)sqlite3_column_text(stmt, 6),
                                                    (const char *)sqlite3_column_text(stmt, 7),
                                                    (const char *)sqlite3_column_text(stmt, 8),
                                                    (const char *)sqlite3_column_text(stmt, 9),
                                                    NULL,
                                                    0,
                                                    (const char *) sqlite3_column_text(stmt, 10));
            raw_product = (char *)sqlite3_column_text(stmt, 11);
            raw_version = (char *)sqlite3_column_text(stmt, 12);
            raw_arch = (char *)sqlite3_column_text(stmt, 13);

            if (wm_vuldet_get_vuln_nvd_cpe(db, agent, flags, stored_cpe, raw_product, raw_version, raw_arch, nvd_report_list)) {
                mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_FIND_NVD_VULN_ERR, raw_product, raw_version);
                wdb_finalize(stmt);
                wm_vuldet_free_cpe(stored_cpe);
                return OS_INVALID;
            }
            wm_vuldet_free_cpe(stored_cpe);
            break;
        default:
            return wm_vuldet_sql_error(db, stmt);
        }
    }

    wdb_finalize(stmt);
    return 0;
}


int wm_vuldet_get_vuln_nvd_cpe(sqlite3 *db, agent_software *agent, wm_vuldet_flags *flags, cpe *agent_cpe, char *raw_product, char *raw_version, char *raw_arch, vu_nvd_report **nvd_report_list) {
    sqlite3_stmt *stmt = NULL;
    sqlite3_stmt *stmt2 = NULL;
    sqlite3_stmt *stmt3 = NULL;
    int result;
    int result2;
    int result3;
    int nvd_cpe_id;
    int nvd_cve_conf;
    int cve_id;
    int prev_nvd_cve_conf;
    int parent;
    char *uri;
    char *cpe_version;
    int vulnerable;
    char *operator;
    char *v_start_inc;
    char *v_start_exc;
    char *v_end_inc;
    char *v_end_exc;
    int retval = OS_INVALID;

    if (wm_vuldet_prepare(db, vu_queries[VU_GET_NVD_CPE], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }
    sqlite3_bind_text(stmt, 1, agent_cpe->part, -1, NULL);
    sqlite3_bind_text(stmt, 2, agent_cpe->vendor, -1, NULL);
    sqlite3_bind_text(stmt, 3, agent_cpe->product, -1, NULL);
    sqlite3_bind_text(stmt, 4, agent_cpe->version, -1, NULL);
    sqlite3_bind_text(stmt, 5, agent_cpe->update ? agent_cpe->update : "", -1, NULL);
    sqlite3_bind_text(stmt, 6, agent_cpe->edition ? agent_cpe->edition : "", -1, NULL);
    sqlite3_bind_text(stmt, 7, agent_cpe->language ? agent_cpe->language : "", -1, NULL);
    sqlite3_bind_text(stmt, 8, agent_cpe->sw_edition ? agent_cpe->sw_edition : "", -1, NULL);
    sqlite3_bind_text(stmt, 9, agent_cpe->target_hw ? agent_cpe->target_hw : "", -1, NULL);

    while(result = wm_vuldet_step(stmt), result != SQLITE_DONE) {
        if (result == SQLITE_ROW) {

            nvd_cpe_id = sqlite3_column_int(stmt, 0);
            cpe_version = (char *) sqlite3_column_text(stmt, 1);

            wdb_finalize(stmt2);

            // For a WIN10 O.S product with a valid product version, discard those
            // CPE's that matched using '-' or '*'.
            if (agent_cpe->part && (agent_cpe->part[0] == 'o') && cpe_version &&
                agent_cpe->vendor && strcmp(cpe_version, agent_cpe->version) &&
                agent->dist_ver == FEED_W10) {
                continue;
            }

            if (wm_vuldet_prepare(db, vu_queries[VU_GET_NVD_MATCHES], -1, &stmt2, NULL) != SQLITE_OK) {
                goto end;
            }
            sqlite3_bind_int(stmt2, 1, nvd_cpe_id);

            prev_nvd_cve_conf = -1;
            while(result2 = wm_vuldet_step(stmt2), result2 != SQLITE_DONE) {
                if (result2 == SQLITE_ROW) {
                    nvd_cve_conf = sqlite3_column_int(stmt2, 0);
                    // Skip if we have found a vulnerability for this configuration and program
                    if (nvd_cve_conf == prev_nvd_cve_conf) {
                        continue;
                    }
                    prev_nvd_cve_conf = -1;

                    uri = (char *)sqlite3_column_text(stmt2, 1);
                    vulnerable = sqlite3_column_int(stmt2, 2);
                    v_start_inc = (char *)sqlite3_column_text(stmt2, 3);
                    v_start_exc = (char *)sqlite3_column_text(stmt2, 4);
                    v_end_inc = (char *)sqlite3_column_text(stmt2, 5);
                    v_end_exc = (char *)sqlite3_column_text(stmt2, 6);

                    wdb_finalize(stmt3);

                    if (wm_vuldet_prepare(db, vu_queries[VU_GET_CONF], -1, &stmt3, NULL) != SQLITE_OK) {
                        goto end;
                    }
                    sqlite3_bind_int(stmt3, 1, nvd_cve_conf);

                    while(result3 = wm_vuldet_step(stmt3), result3 != SQLITE_DONE) {
                        if (result3 == SQLITE_ROW) {
                            cpe *cpe_node;
                            vu_nvd_report *nvd_report_node;
                            cve_id = sqlite3_column_int(stmt3, 0);
                            operator = (char *)sqlite3_column_text(stmt3, 1);
                            parent = sqlite3_column_int(stmt3, 2);

                            if (!strstr(uri, CPE_VER_TAG)) {
                                continue;
                            }

                            cpe_node = wm_vuldet_decode_cpe(uri + strlen(CPE_VER_TAG));

                            nvd_report_node = wm_vuldet_check_nvd_vulnerability(db, agent, flags, agent_cpe, cve_id, nvd_cve_conf, vulnerable,
                                                                                operator, parent, cpe_node->version,
                                                                                v_start_inc, v_start_exc, v_end_inc, v_end_exc);
                            if (nvd_report_node) {
                                if (nvd_report_node->vulnerable) {
                                    nvd_report_node->id = cve_id;
                                    if (*nvd_report_list) {
                                        nvd_report_node->prev = *nvd_report_list;
                                    }
                                    *nvd_report_list = nvd_report_node;

                                    if (nvd_report_node->vulnerable) {
                                        nvd_report_node->id = cve_id;
                                        os_strdup(raw_product, nvd_report_node->raw_product);
                                        os_strdup(raw_version, nvd_report_node->raw_version);
                                        os_strdup(raw_arch, nvd_report_node->raw_arch);
                                        nvd_report_node->generated_cpe = wm_vuldet_cpe_str(agent_cpe);
                                    }
                                    prev_nvd_cve_conf = nvd_cve_conf;
                                } else {
                                    wm_vuldet_free_nvd_report(nvd_report_node);
                                    free(nvd_report_node);
                                }
                            }
                            wm_vuldet_free_cpe(cpe_node);
                        } else {
                            goto end;
                        }
                    }
                } else {
                    goto end;
                }
            }
        } else {
            goto end;
        }
    }

    retval = 0;
end:
    if (retval) {
        wm_vuldet_sql_error(db, NULL);
    }

    wdb_finalize(stmt);
    wdb_finalize(stmt2);
    wdb_finalize(stmt3);

    return retval;
}

vu_nvd_report *wm_vuldet_check_nvd_vulnerability(sqlite3 *db, agent_software *agent, wm_vuldet_flags *flags, cpe *agent_cpe, int cve_id, int conf_id, int vulnerable, char *operator, int parent, char *uri_version, char *v_start_inc, char *v_start_exc, char *v_end_inc, char *v_end_exc) {
    vu_nvd_report *nvd_report_node = NULL;

    if (!vulnerable) {
        return nvd_report_node;
    }

    os_calloc(1, sizeof(vu_nvd_report), nvd_report_node);
    nvd_report_node->vulnerable = 0;
    w_strdup(operator, nvd_report_node->operator);
    w_strdup(agent_cpe->version, nvd_report_node->version);
    w_strdup(v_start_inc, nvd_report_node->v_start_inc);
    w_strdup(v_start_exc, nvd_report_node->v_start_exc);
    w_strdup(v_end_inc, nvd_report_node->v_end_inc);
    w_strdup(v_end_exc, nvd_report_node->v_end_exc);

    if (wm_vulndet_undefined_cond(v_start_inc) &&
        wm_vulndet_undefined_cond(v_start_exc) &&
        wm_vulndet_undefined_cond(v_end_inc) &&
        wm_vulndet_undefined_cond(v_end_exc)) {
        int retval = wm_vuln_valid_os_cpe(agent->dist_ver, agent_cpe, uri_version);

        if (retval == 2) {
            // The OS cpe does not match
            nvd_report_node->vulnerable = 0;
            goto end;
        } else if (retval == 1 && wm_vuldet_is_cpe_wc(uri_version)) {
            // Pending
            nvd_report_node->pending = 1;
            goto end;
        }

        if (agent_cpe->check_hotfix) {
            if (flags->patch_scan &&
                wm_vuldet_check_hotfix(db, agent, flags, cve_id,
                agent_cpe, &nvd_report_node->necessary_patch) == VU_VULNERABLE) {
                nvd_report_node->vulnerable = 1;
            }
        } else {
            nvd_report_node->vulnerable = 1;
        }
    } else {
        vu_logic s_inc = v_start_inc ? wm_checks_package_vulnerability(agent_cpe->version, vu_package_comp[VU_COMP_GE], v_start_inc, VER_TYPE_NVD)
                                     : VU_UNDEFINED;
        vu_logic s_exc = v_start_exc ? wm_checks_package_vulnerability(agent_cpe->version, vu_package_comp[VU_COMP_G], v_start_exc, VER_TYPE_NVD)
                                     : VU_UNDEFINED;
        vu_logic e_inc = v_end_inc ? wm_checks_package_vulnerability(agent_cpe->version, vu_package_comp[VU_COMP_LE], v_end_inc, VER_TYPE_NVD)
                                   : VU_UNDEFINED;
        vu_logic e_exc = v_end_exc ? wm_checks_package_vulnerability(agent_cpe->version, vu_package_comp[VU_COMP_L], v_end_exc, VER_TYPE_NVD)
                                   : VU_UNDEFINED;

        if ((s_inc == VU_UNDEFINED || s_inc == VU_VULNERABLE) &&
            (s_exc == VU_UNDEFINED || s_exc == VU_VULNERABLE) &&
            (e_inc == VU_UNDEFINED || e_inc == VU_VULNERABLE) &&
            (e_exc == VU_UNDEFINED || e_exc == VU_VULNERABLE)) {
            nvd_report_node->vulnerable = 1;
        }
    }

    if (nvd_report_node->vulnerable && parent) {
        wm_vuldet_check_nvd_logical(db, agent->agent_id, nvd_report_node, conf_id, parent);
    }

end:
    return nvd_report_node;
}

int wm_vuldet_report_nvd_vulnerabilities(sqlite3 *db, vu_nvd_report **nvd_report, agent_software *agent) {
    sqlite3_stmt *stmt = NULL;
    vu_nvd_report *report_node;
    vu_nvd_report *f_report_node;
    vu_report *report = NULL;
    int result;
    char *cve;
    char *cwe;
    char *assigner;
    char *description;
    char *cve_version;
    char *published;
    char *last_mod;
    char *url;
    char *ref_source;
    int retval = OS_INVALID;

    report_node = *nvd_report;
    while(report_node) {
        wm_vuldet_free_report(report);
        os_calloc(1, sizeof(vu_report), report);
        if (wm_vuldet_prepare(db, vu_queries[VU_GET_CVE_INFO], -1, &stmt, NULL) != SQLITE_OK) {
            goto error;
        }
        sqlite3_bind_int(stmt, 1, report_node->id);

        result = wm_vuldet_step(stmt);
        if (result == SQLITE_ROW) {
            cve = (char *)sqlite3_column_text(stmt, 0);
            cwe = (char *)sqlite3_column_text(stmt, 1);
            assigner = (char *)sqlite3_column_text(stmt, 2);
            description = (char *)sqlite3_column_text(stmt, 3);
            cve_version = (char *)sqlite3_column_text(stmt, 4);
            published = (char *)sqlite3_column_text(stmt, 5);
            last_mod = (char *)sqlite3_column_text(stmt, 6);

            os_strdup(cve, report->cve);
            os_strdup(cwe, report->cwe);
            os_strdup(assigner, report->assigner);
            os_strdup(description, report->rationale);
            os_strdup(cve_version, report->cve_version);
            os_strdup(published, report->published);
            os_strdup(last_mod, report->updated);

        } else {
            goto error;
        }

        wdb_finalize(stmt);

        if (wm_vuldet_prepare(db, vu_queries[VU_GET_REFERENCE], -1, &stmt, NULL) != SQLITE_OK) {
            goto error;
        }
        sqlite3_bind_int(stmt, 1, report_node->id);

        int ref_count = 0;
        while (wm_vuldet_step(stmt) == SQLITE_ROW) {
            ref_source = (char *)sqlite3_column_text(stmt, 1);
            url = (char *)sqlite3_column_text(stmt, 0);

            os_realloc(report->ref_sources, (ref_count + 2) * sizeof(char *), report->ref_sources);
            os_strdup(ref_source, report->ref_sources[ref_count]);
            os_realloc(report->references, (ref_count + 2) * sizeof(char *), report->references);
            os_strdup(url, report->references[ref_count]);
            ++ref_count;
            report->ref_sources[ref_count] = report->references[ref_count] = NULL;
        }
        wdb_finalize(stmt);

        if (wm_vuldet_prepare(db, vu_queries[VU_GET_SCORING], -1, &stmt, NULL) != SQLITE_OK) {
            goto error;
        }
        sqlite3_bind_int(stmt, 1, report_node->id);

        while (wm_vuldet_step(stmt) == SQLITE_ROW) {
            cv_scoring_system **cvss;
            char *vector_string = (char *)sqlite3_column_text(stmt, 0);
            double base_score = sqlite3_column_double(stmt, 1);
            double exploitability_score = sqlite3_column_double(stmt, 2);
            double impact_score = sqlite3_column_double(stmt, 3);
            char *cvss_ver = (char *)sqlite3_column_text(stmt, 4);

            if (!cvss_ver) {
                continue;
            }

            if (strstr(cvss_ver, "2.") && !report->cvss2) {
                cvss = &report->cvss2;
            } else if (strstr(cvss_ver, "3.") && !report->cvss3) {
                cvss = &report->cvss3;
            } else {
                continue;
            }

            os_calloc(1, sizeof(cv_scoring_system), *cvss);
            w_strdup(vector_string, (*cvss)->vector_string);
            (*cvss)->base_score = base_score;
            (*cvss)->exploitability_score = exploitability_score;
            (*cvss)->impact_score = impact_score;
        }
        wdb_finalize(stmt);

        if (report->cvss3) {
            if (report->cvss3->base_score >= 9) {
                os_strdup(vu_severities[VU_CRITICAL], report->severity);
            } else if (report->cvss3->base_score >= 7) {
                os_strdup(vu_severities[VU_HIGH], report->severity);
            } else if (report->cvss3->base_score >= 4) {
                os_strdup(vu_severities[VU_MEDIUM], report->severity);
            } else if (report->cvss3->base_score >= 0.1) {
                os_strdup(vu_severities[VU_LOW], report->severity);
            } else {
                os_strdup(vu_severities[VU_NONE], report->severity);
            }
        } else if (report->cvss2) {
            if (report->cvss2->base_score >= 7) {
                os_strdup(vu_severities[VU_HIGH], report->severity);
            } else if (report->cvss2->base_score >= 4) {
                os_strdup(vu_severities[VU_MEDIUM], report->severity);
            } else {
                os_strdup(vu_severities[VU_LOW], report->severity);
            }
        } else {
            os_strdup(vu_severities[VU_UNKNOWN], report->severity);
        }

        os_strdup(report_node->raw_product, report->software);
        os_strdup(report_node->raw_version, report->version);
        os_strdup(report_node->generated_cpe, report->generated_cpe);
        os_strdup(report_node->raw_arch, report->arch);
        report->pending = report_node->pending;
        wm_vuldet_build_nvd_condition(report_node, &report->condition, &report->is_hotfix);

        f_report_node = report_node;
        report_node = report_node->prev;
        *nvd_report = report_node;
        w_strdup(agent->agent_id, report->agent_id);
        w_strdup(agent->agent_name, report->agent_name);
        w_strdup(agent->agent_ip, report->agent_ip);

        // Build title
        os_calloc(OS_SIZE_512, sizeof(char), report->title);
        snprintf(report->title, OS_SIZE_512, "%s affects %s", report->cve, report->software);

        wm_vuldet_send_cve_report(report);
        wm_vuldet_free_nvd_report(f_report_node);
        free(f_report_node);
    }

    retval = 0;
error:
    wm_vuldet_free_report(report);
    return retval ? wm_vuldet_sql_error(db, stmt) : 0;
}

char *wm_vuldet_clean_version(const char *version) {
    char *data  = NULL;
    char *dup   = NULL;
    char *clean_version = NULL;

    os_strdup(version, dup);

    // Store the data between : and -
    strtok(dup, ":");
    data = strtok(NULL, ":");
    data = strtok((data) ? data : dup, "-");

    w_strdup(data, clean_version);
    os_free(dup);

    return clean_version;
}

int vw_vuldet_filter_vulnerabilities(vu_feed feed, const char *vendor, const char *target_sw) {
    uint8_t target_sw_it = 0;
    uint8_t target_sw_len = 0;
    char ** target = NULL;

    if (vendor) {
        // Check vendor against feed
        if (feed == FEED_REDHAT) {
            if (!strcmp(V_UBUNTU, vendor) || !strcmp(V_DEBIAN, vendor)) {
                return 1;
            }
        } else if (!strcmp(V_REDHAT, vendor)) {
            return 1;
        }
    }

    if (target_sw) {
        // Check target_sw whitelist
        target = (feed != FEED_MAC)? (char **)vu_linux_target_sw_wl : (char **)vu_mac_target_sw_wl;
        target_sw_len = (feed != FEED_MAC)? array_size(vu_linux_target_sw_wl) : array_size(vu_mac_target_sw_wl);

        for(target_sw_it = 0; target_sw_it < target_sw_len; ++target_sw_it) {
            if(!strcmp(target[target_sw_it], target_sw)) {
                return 0;
            }
        }
    }

    return 1;
}

int wm_vuldet_check_generic_package(sqlite3 *dbCVE, vu_feed feed, char *pkg_name, char *pkg_source, char *pkg_version, char *pkg_arch, char *pkg_vendor, OSHash *cve_table, int8_t name_type, int *vuln_count) {
    sqlite3_stmt *stmt = NULL;
    const char *query = NULL;
    bool vulnerable_global = false;
    bool vulnerable = false;
    bool search_vulnerabilities = false;
    bool insert_package = true;
    bool os_package = false;
    cve_vuln_pkg *newPkg = NULL;
    char *pkg_reference = NULL;
    const char *operation;
    const char *operation_value;
    bool app_package_mac = false;

    pkg_reference = (name_type == PACKAGE_SOURCE) ? pkg_source : pkg_name;

    if (!strcmp(pkg_reference, PR_UBUNTU) ||
        !strcmp(pkg_reference, PR_DEBIAN) ||
        !strcmp(pkg_reference, PR_REDHAT) ||
        !strcmp(pkg_reference, PR_KERNEL)) {
        query = vu_queries[VU_GET_GENERIC_PACKAGE_OS];
        os_package = true;
    } else if (!strcmp(pkg_reference, "mac_os_x") || !strcmp(pkg_reference, "mac_os_x_server") ||
               !strcmp(pkg_reference, "mac_os") || !strcmp(pkg_reference, "macos")) {
        query = vu_queries[VU_GET_GENERIC_PACKAGE_OS_MAC];
    } else {
        if (feed == FEED_MAC) {
            query = vu_queries[VU_GET_GENERIC_PACKAGE_APP_MAC];
            app_package_mac = true;
        } else {
            query = vu_queries[VU_GET_GENERIC_PACKAGE_APP];
        }
    }

    if (wm_vuldet_prepare(dbCVE, query, -1, &stmt, NULL) != SQLITE_OK) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_GET_PACKAGES_VULN_ERROR, "generic", pkg_reference);
        return wm_vuldet_sql_error(dbCVE, stmt);
    }

    sqlite3_bind_text(stmt, 1, pkg_reference, -1, NULL);
    if (app_package_mac) {
        /* Selecting all vulnerabilities for a mac app package if vendor is null */
        if (!pkg_vendor) {
            sqlite3_bind_text(stmt, 2, "", -1, NULL);
            sqlite3_bind_text(stmt, 3, "", -1, NULL);
        } else {
            sqlite3_bind_text(stmt, 2, pkg_vendor, -1, NULL);
            sqlite3_bind_text(stmt, 3, pkg_vendor, -1, NULL);
        }
    } else {
        if (!strcmp(pkg_reference, PR_UBUNTU)) {
            sqlite3_bind_text(stmt, 2, V_UBUNTU, -1, NULL);
        } else if (!strcmp(pkg_reference, PR_DEBIAN)) {
            sqlite3_bind_text(stmt, 2, V_DEBIAN, -1, NULL);
        } else if (!strcmp(pkg_reference, PR_REDHAT)) {
            sqlite3_bind_text(stmt, 2, V_REDHAT, -1, NULL);
        } else if (!strcmp(pkg_reference, PR_KERNEL)) {
            sqlite3_bind_text(stmt, 2, V_KERNEL, -1, NULL);
        }
    }

    while (SQLITE_ROW == wm_vuldet_step(stmt)) {
        const char *cve = (const char *) sqlite3_column_text(stmt, 0);
        const char *start_including = (const char *) sqlite3_column_text(stmt, 1);
        const char *start_excluding = (const char *) sqlite3_column_text(stmt, 2);
        const char *end_including = (const char *) sqlite3_column_text(stmt, 3);
        const char *end_excluding = (const char *) sqlite3_column_text(stmt, 4);
        const int package_id = (const int) sqlite3_column_int(stmt, 5);
        const int configuration_id = (const int) sqlite3_column_int(stmt, 6);
        const int package_vulnerable = (const int) sqlite3_column_int(stmt, 7);
        const int parent = (const int) sqlite3_column_int(stmt, 8);
        const char *operator = (const char *) sqlite3_column_text(stmt, 9);
        const char *vendor = (const char *) sqlite3_column_text(stmt, 10);
        const char *target_sw = (const char *) sqlite3_column_text(stmt, 11);

        // First, we filter by vendor and target_sw
        if (vw_vuldet_filter_vulnerabilities(feed, vendor, target_sw)) {
            continue;
        }

        // Analize the vulnerability more in detail only if
        // the pkg's version is greater than the start_*
        if (start_including) {
            if (wm_checks_package_vulnerability(pkg_version, vu_package_comp[PKG_GREATER_THAN_OR_EQUAL], start_including, VER_TYPE_NVD) == VU_VULNERABLE) {
                search_vulnerabilities = true;
            }
            operation = vu_package_comp[PKG_GREATER_THAN_OR_EQUAL];
            operation_value = start_including;
        }

        else if (start_excluding) {
            if (wm_checks_package_vulnerability(pkg_version, vu_package_comp[PKG_GREATER_THAN], start_excluding, VER_TYPE_NVD) == VU_VULNERABLE) {
                search_vulnerabilities = true;
            }
            operation = vu_package_comp[PKG_GREATER_THAN];
            operation_value = start_excluding;
        }

        else if(!start_including && !start_excluding) {
            search_vulnerabilities = true;
            operation = vu_package_comp[PKG_EQUAL];
            operation_value = "*";
        }

        // If the pkg's version is between a start_*
        // and an end_*, mark it as vulnerable.
        if(search_vulnerabilities) {
            if (!end_including && !end_excluding) { // If there is no end_*, it means there is no known fix.
                vulnerable_global = vulnerable = true;
            }

            else if (end_including) {
                if (wm_checks_package_vulnerability(pkg_version, vu_package_comp[PKG_LESS_THAN_OR_EQUAL], end_including, VER_TYPE_NVD) == VU_VULNERABLE) {
                    vulnerable_global = vulnerable = true;
                }
                operation = vu_package_comp[PKG_LESS_THAN_OR_EQUAL];
                operation_value = end_including;
            }

            else if (end_excluding) {
                if (wm_checks_package_vulnerability(pkg_version, vu_package_comp[PKG_LESS_THAN], end_excluding, VER_TYPE_NVD) == VU_VULNERABLE) {
                    vulnerable_global = vulnerable = true;
                }
                operation = vu_package_comp[PKG_LESS_THAN];
                operation_value = end_excluding;
            }
        }

        // Insert the vulnerable package into the hash table.
        if (vulnerable == true) {
            os_calloc(1, sizeof(cve_vuln_pkg), newPkg);
            os_calloc(1, sizeof(cve_vuln_cond_NVD), newPkg->nvd_cond);

            // Get dependencies for further study.
            if (parent == 0 && !strcmp(operator, "AND")) { // Get children
                int vuln_children = wm_vuldet_get_children(dbCVE, configuration_id, package_id, newPkg->nvd_cond->children);
                if(vuln_children == OS_INVALID) {
                    goto end;
                }
            }

            else if (parent != 0 && !strcmp(operator, "OR")) { // Get siblings
                int vuln_siblings = wm_vuldet_get_siblings(dbCVE, parent, configuration_id, newPkg->nvd_cond->siblings);
                if(vuln_siblings == OS_INVALID) {
                    goto end;
                }
            }

            sqlite_strdup(pkg_version, newPkg->version);
            sqlite_strdup(pkg_name, newPkg->bin_name);
            sqlite_strdup(pkg_source, newPkg->src_name);
            sqlite_strdup(pkg_arch, newPkg->arch);
            sqlite_strdup(pkg_vendor, newPkg->vendor);

            sqlite_strdup(operator, newPkg->nvd_cond->operator);
            newPkg->nvd_cond->parent = parent;
            newPkg->nvd_cond->vulnerable = (package_vulnerable) ? true : false;
            newPkg->nvd_cond->id = package_id;
            newPkg->nvd_cond->conf_id = configuration_id;
            newPkg->feed |= VU_SRC_NVD;

            sqlite_strdup((end_including) ? end_including : end_excluding,
                            newPkg->nvd_cond->end_version);
            sqlite_strdup((start_including) ? start_including : start_excluding,
                            newPkg->nvd_cond->start_version);

            if (newPkg->nvd_cond->end_version) {
                newPkg->nvd_cond->end_operation = (end_including) ? END_INCLUDED : END_EXCLUDED;
            }
            if (newPkg->nvd_cond->start_version) {
                newPkg->nvd_cond->start_operation = (start_including) ? START_INCLUDED : START_EXCLUDED;
            }

            insert_package = true;

            // We will use the OS and generic packages only to check dependencies
            if (os_package || (!newPkg->nvd_cond->end_version && !newPkg->nvd_cond->start_version)) {
                if (*newPkg->nvd_cond->children || *newPkg->nvd_cond->siblings) {
                    // We will discard this package later (after check dependencies)
                    newPkg->nvd_cond->vulnerable = 0;
                } else {
                    // We ignore this package
                    insert_package = false;
                }
            }

            if (insert_package) {
                int result = wm_vuldet_add_cve_node(newPkg, cve, cve_table);

                switch (result) {
                case -1:
                    mterror(WM_VULNDETECTOR_LOGTAG, VU_INSERT_PACKAGE_ERROR, pkg_reference, cve, pkg_version, operation, operation_value, "NVD");
                    wm_vuldet_free_cve_node(newPkg);
                    break;
                case 1:
                    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_DUPLICATED_PACKAGE, pkg_reference, cve, pkg_version, operation, operation_value, "NVD");
                    (*vuln_count)++;
                    wm_vuldet_free_cve_node(newPkg);
                    break;
                case 0:
                    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_INSERT, pkg_reference, cve, pkg_version, operation, operation_value, "NVD");
                    (*vuln_count)++;
                    break;
                }
            } else {
                wm_vuldet_free_cve_node(newPkg);
            }

            newPkg = NULL;
        } else {
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_NOT_VULN, pkg_reference, cve, pkg_version, operation, operation_value, "NVD");
        }

        search_vulnerabilities = false;
        vulnerable = false;
    }

    wdb_finalize(stmt);

    return(vulnerable_global) ? 1 : 0;

end:
    wdb_finalize(stmt);
    os_free(newPkg->nvd_cond);
    os_free(newPkg);
    return OS_INVALID;
}

int wm_vuldet_check_specific_package(sqlite3 *dbCVE, vu_feed feed, char *pkg_name, char *pkg_source, char *pkg_version, char *pkg_arch, char *pkg_vendor, OSHash *cve_table, int8_t name_type, int *vuln_count) {
    sqlite3_stmt *stmt = NULL;
    const char *query = NULL;
    bool vulnerable = false;
    bool insert_package = true;
    bool os_package = false;
    cve_vuln_pkg *newPkg = NULL;
    char *pkg_reference = NULL;
    char *sql_version = NULL;
    char *clean_version = NULL;
    size_t len = 0;
    bool app_package_mac = false;

    pkg_reference = (name_type == PACKAGE_SOURCE) ? pkg_source : pkg_name;

    if (!pkg_version) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_PKG_NO_VER, pkg_reference);
        return 0;
    }

    // Use a cleaned version without epoch and release so we can use a regex
    // within the sql query to gather all possible combinations (NVD does not store epoch).
    clean_version = wm_vuldet_clean_version(pkg_version);
    len = strlen(clean_version) + 3;
    os_calloc(1, len, sql_version);
    snprintf(sql_version, len, "%%%s%%", clean_version);
    os_free(clean_version);

    if (!strcmp(pkg_reference, PR_UBUNTU) ||
        !strcmp(pkg_reference, PR_DEBIAN) ||
        !strcmp(pkg_reference, PR_REDHAT) ||
        !strcmp(pkg_reference, PR_KERNEL)) {
        query = vu_queries[VU_GET_SPECIFIC_PACKAGE_OS];
        os_package = true;
    } else if (!strcmp(pkg_reference, "mac_os_x") || !strcmp(pkg_reference, "mac_os_x_server") ||
               !strcmp(pkg_reference, "mac_os") || !strcmp(pkg_reference, "macos")) {
        query = vu_queries[VU_GET_SPECIFIC_PACKAGE_OS_MAC];
        os_package = true;
    } else {
        if (feed == FEED_MAC) {
            query = vu_queries[VU_GET_SPECIFIC_PACKAGE_APP_MAC];
            app_package_mac = true;
        } else {
            query = vu_queries[VU_GET_SPECIFIC_PACKAGE_APP];
        }
    }

    if (wm_vuldet_prepare(dbCVE, query, -1, &stmt, NULL) != SQLITE_OK) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_GET_PACKAGES_VULN_ERROR, "specific", pkg_reference);
        os_free(sql_version);
        return wm_vuldet_sql_error(dbCVE, stmt);
    }

    sqlite3_bind_text(stmt, 1, pkg_reference, -1, NULL);
    sqlite3_bind_text(stmt, 2, sql_version, -1, NULL);

    if (app_package_mac) {
        /* Selecting all vulnerabilities for a mac app package if vendor is null */
        if (!pkg_vendor) {
            sqlite3_bind_text(stmt, 3, "", -1, NULL);
            sqlite3_bind_text(stmt, 4, "", -1, NULL);
        } else {
            sqlite3_bind_text(stmt, 3, pkg_vendor, -1, NULL);
            sqlite3_bind_text(stmt, 4, pkg_vendor, -1, NULL);
        }
    } else {
        if (!strcmp(pkg_reference, PR_UBUNTU)) {
            sqlite3_bind_text(stmt, 3, V_UBUNTU, -1, NULL);
        } else if (!strcmp(pkg_reference, PR_DEBIAN)) {
            sqlite3_bind_text(stmt, 3, V_DEBIAN, -1, NULL);
        } else if (!strcmp(pkg_reference, PR_REDHAT)) {
            sqlite3_bind_text(stmt, 3, V_REDHAT, -1, NULL);
        } else if (!strcmp(pkg_reference, PR_KERNEL)) {
            sqlite3_bind_text(stmt, 3, V_KERNEL, -1, NULL);
        }
    }

    while (SQLITE_ROW == wm_vuldet_step(stmt)) {
        const char *cve = (const char *)sqlite3_column_text(stmt, 0);
        const char *version_cmp = (const char *)sqlite3_column_text(stmt, 1);
        const int package_id = sqlite3_column_int(stmt, 2);
        const int configuration_id = sqlite3_column_int(stmt, 3);
        const int package_vulnerable = sqlite3_column_int(stmt, 4);
        const int parent = sqlite3_column_int(stmt, 5);
        const char *operator = (const char *) sqlite3_column_text(stmt, 6);
        const char *vendor = (const char *) sqlite3_column_text(stmt, 7);
        const char *target_sw = (const char *) sqlite3_column_text(stmt, 8);

        // First, we filter by vendor and target_sw
        if (vw_vuldet_filter_vulnerabilities(feed, vendor, target_sw)) {
            continue;
        }

        // Find out if the pkg is vulnerable and insert it into the hash table.
        if (wm_checks_package_vulnerability((char *)pkg_version,
            vu_package_comp[PKG_EQUAL], version_cmp, VER_TYPE_NVD) == VU_VULNERABLE) {
            os_calloc(1, sizeof(cve_vuln_pkg), newPkg);
            os_calloc(1, sizeof(cve_vuln_cond_NVD), newPkg->nvd_cond);

            // Get dependencies for further study.
            if (parent == 0 && !strcmp(operator, "AND")) { // Get children
                int vuln_children = wm_vuldet_get_children(dbCVE, configuration_id, package_id, newPkg->nvd_cond->children);
                if(vuln_children == OS_INVALID) {
                    goto end;
                }
            }

            else if (parent != 0 && !strcmp(operator, "OR")) { // Get siblings
                int vuln_siblings = wm_vuldet_get_siblings(dbCVE, parent, configuration_id, newPkg->nvd_cond->siblings);
                if(vuln_siblings == OS_INVALID) {
                    goto end;
                }
            }

            sqlite_strdup(pkg_version, newPkg->version);
            sqlite_strdup(pkg_name, newPkg->bin_name);
            sqlite_strdup(pkg_source, newPkg->src_name);
            sqlite_strdup(pkg_arch, newPkg->arch);
            sqlite_strdup(pkg_vendor, newPkg->vendor);

            sqlite_strdup(operator, newPkg->nvd_cond->operator);
            newPkg->nvd_cond->parent = parent;
            newPkg->nvd_cond->vulnerable = (package_vulnerable) ? true : false;
            newPkg->nvd_cond->id = package_id;
            newPkg->nvd_cond->conf_id = configuration_id;
            newPkg->feed |= VU_SRC_NVD;

            insert_package = true;

            // We will use the OS packages only to check dependencies
            if (os_package) {
                if (*newPkg->nvd_cond->children || *newPkg->nvd_cond->siblings) {
                    // We will discard this package later (after check dependencies)
                    newPkg->nvd_cond->vulnerable = 0;
                } else {
                    // We ignore this package
                    insert_package = false;
                }
            }

            if (insert_package) {
                int result = wm_vuldet_add_cve_node(newPkg, cve, cve_table);

                switch (result) {
                case -1:
                    mterror(WM_VULNDETECTOR_LOGTAG, VU_INSERT_PACKAGE_ERROR, pkg_reference, cve, pkg_version, vu_package_comp[PKG_EQUAL], version_cmp, "NVD");
                    wm_vuldet_free_cve_node(newPkg);
                    break;
                case 1:
                    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_DUPLICATED_PACKAGE, pkg_reference, cve, pkg_version, vu_package_comp[PKG_EQUAL], version_cmp, "NVD");
                    (*vuln_count)++;
                    wm_vuldet_free_cve_node(newPkg);
                    break;
                case 0:
                    mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_INSERT, pkg_reference, cve, pkg_version, vu_package_comp[PKG_EQUAL], version_cmp, "NVD");
                    (*vuln_count)++;
                    break;
                }
            } else {
                wm_vuldet_free_cve_node(newPkg);
            }

            newPkg = NULL;
            vulnerable = true;
        } else {
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_PACKAGE_NOT_VULN, pkg_reference, cve, pkg_version, vu_package_comp[PKG_EQUAL], version_cmp, "NVD");
        }
    }

    wdb_finalize(stmt);
    os_free(sql_version);

    return(vulnerable) ? 1 : 0;

end:
    wdb_finalize(stmt);
    os_free(sql_version);
    os_free(newPkg->nvd_cond);
    os_free(newPkg);
    return OS_INVALID;
}

int wm_vuldet_get_children(sqlite3 * dbCVE, int configuration_id, int package_id, int *children) {
    sqlite3_stmt *stmt = NULL;
    int i;

    if (wm_vuldet_prepare(dbCVE, vu_queries[VU_GET_CHILDREN], -1, &stmt, NULL) != SQLITE_OK) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_GET_PACKAGES_DEP_ERROR, "children", package_id);
        return wm_vuldet_sql_error(dbCVE, stmt);
    }

    sqlite3_bind_int(stmt, 1, configuration_id);
    sqlite3_bind_int(stmt, 2, package_id);

    for(i = 0; SQLITE_ROW == wm_vuldet_step(stmt) && i < MAX_RELATED_PKGS; i++) {
        children[i] = sqlite3_column_int(stmt, 0);
    }

    wdb_finalize(stmt);

    return 0;
}

int wm_vuldet_get_siblings(sqlite3 * dbCVE, int parent, int configuration_id, int *siblings) {
    sqlite3_stmt *stmt = NULL;
    int i;

    if (wm_vuldet_prepare(dbCVE, vu_queries[VU_GET_SIBLINGS], -1, &stmt, NULL) != SQLITE_OK) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_GET_PACKAGES_DEP_ERROR, "siblings", configuration_id);
        return wm_vuldet_sql_error(dbCVE, stmt);
    }

    sqlite3_bind_int(stmt, 1, parent);
    sqlite3_bind_int(stmt, 2, configuration_id);

    for(i = 0; SQLITE_ROW == wm_vuldet_step(stmt) && i < MAX_RELATED_PKGS; i++) {
        siblings[i] = sqlite3_column_int(stmt, 0);
    }

    wdb_finalize(stmt);

    return 0;
}

void wm_vuldet_build_nvd_condition(vu_nvd_report *report, char **condition, char *is_hotfix) {
    const char *operation = NULL;
    char *operation_value = NULL;
    const char *s_operation = NULL;
    char *s_operation_value = NULL;
    size_t size = 0;
    *condition = NULL;

    if (report->necessary_patch) {
        if (*report->necessary_patch == '|') {
            os_strdup("No hotfix was found to fix the vulnerability in the Microsoft database.", *condition);
        } else {
            const char *patch_msg = "patch is not installed.";
            size = strlen(patch_msg) + strlen(report->necessary_patch) + 3;
            os_calloc(size + 1, sizeof(char), *condition);
            snprintf(*condition, size, "KB%s %s", report->necessary_patch, patch_msg);
            *is_hotfix = 1;
        }

        return;
    }

    if (report->v_start_inc) {
        operation = vu_package_comp[VU_COMP_GE];
        operation_value = report->v_start_inc;
    } else if (report->v_start_exc) {
        operation = vu_package_comp[VU_COMP_G];
        operation_value = report->v_start_exc;
    }

    if (report->v_end_inc) {
        s_operation = vu_package_comp[VU_COMP_LE];
        s_operation_value = report->v_end_inc;
    } else if (report->v_end_exc) {
        s_operation = vu_package_comp[VU_COMP_L];
        s_operation_value = report->v_end_exc;
    }

    if (operation || s_operation || report->and_package) {
        size = (operation ? strlen(operation) + w_strlen(operation_value) + 5 : 0)
                + (s_operation ? strlen(s_operation) + w_strlen(s_operation_value) + 5 : 0);
        if (report->and_package) {
            size += strlen(report->and_package);
        }
        os_calloc(size + 1, sizeof(char), *condition);
        snprintf(*condition, size, "%s%s%s%s%s%s%s%s",
            operation ? operation : "",
            operation ? " " : "",
            operation ? operation_value : "",
            operation && s_operation ? " and " : "",
            s_operation ? s_operation : "",
            s_operation ? " " : "",
            s_operation ? s_operation_value : "",
            report->and_package ? report->and_package : ""
            );
    }

    if (!*condition) {
        os_strdup("Version match", *condition);
    }
}

void wm_vuldet_free_nvd_report(vu_nvd_report *report) {
    free(report->raw_product);
    free(report->raw_version);
    free(report->raw_arch);
    free(report->operator);
    free(report->version);
    free(report->necessary_patch);
    free(report->v_start_inc);
    free(report->v_start_exc);
    free(report->v_end_inc);
    free(report->v_end_exc);
    free(report->generated_cpe);
}

void wm_vuldet_free_nvd_report_list(vu_nvd_report *nvd_report_list) {
    if (nvd_report_list) {
        while (nvd_report_list) {
            vu_nvd_report *f_report = nvd_report_list;
            nvd_report_list = nvd_report_list->prev;
            wm_vuldet_free_nvd_report(f_report);
            free(f_report);
        }
    }
}

int wm_vuldet_check_nvd_logical(sqlite3 *db, char *agent_id, vu_nvd_report *rp, int conf_id, int parent) {
    sqlite3_stmt *stmt_conf = NULL;
    sqlite3_stmt *stmt_match = NULL;
    sqlite3_stmt *stmt_cpe = NULL;
    sqlite3_stmt *stmt_agentcpe = NULL;
    int result_conf;
    int result_match;
    int result_cpe;
    int result_agentcpe;
    int and_conf;
    int and_mach;
    char *vendor;
    char *product;
    //char *version;

    rp->vulnerable = 0;

    if (wm_vuldet_prepare(db, vu_queries[VU_GET_CONF_AND], -1, &stmt_conf, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt_conf);
    }
    sqlite3_bind_int(stmt_conf, 1, parent);
    sqlite3_bind_int(stmt_conf, 2, conf_id);

    while(result_conf = wm_vuldet_step(stmt_conf), result_conf != SQLITE_DONE) {
        switch (result_conf) {
        case SQLITE_ROW:
            and_conf = sqlite3_column_int(stmt_conf, 0);

            if (wm_vuldet_prepare(db, vu_queries[VU_GET_MATCHES_AND], -1, &stmt_match, NULL) != SQLITE_OK) {
                return wm_vuldet_sql_error(db, stmt_match);
            }
            sqlite3_bind_int(stmt_match, 1, and_conf);

            while(result_match = wm_vuldet_step(stmt_match), result_match != SQLITE_DONE) {
                switch (result_match) {
                case SQLITE_ROW:
                    and_mach = sqlite3_column_int(stmt_match, 0);

                    if (wm_vuldet_prepare(db, vu_queries[VU_GET_CPE_AND], -1, &stmt_cpe, NULL) != SQLITE_OK) {
                        return wm_vuldet_sql_error(db, stmt_cpe);
                    }
                    sqlite3_bind_int(stmt_match, 1, and_mach);

                    while(result_cpe = wm_vuldet_step(stmt_cpe), result_cpe != SQLITE_DONE) {
                        switch (result_cpe) {
                        case SQLITE_ROW:
                            vendor = (char *)sqlite3_column_text(stmt_cpe, 0);
                            product = (char *)sqlite3_column_text(stmt_cpe, 1);

                            if (wm_vuldet_prepare(db, vu_queries[VU_GET_AGENTCPE_AND], -1, &stmt_agentcpe, NULL) != SQLITE_OK) {
                                return wm_vuldet_sql_error(db, stmt_agentcpe);
                            }
                            sqlite3_bind_text(stmt_agentcpe, 1, vendor, -1, NULL);
                            sqlite3_bind_text(stmt_agentcpe, 2, product, -1, NULL);
                            sqlite3_bind_text(stmt_agentcpe, 3, agent_id, -1, NULL);

                            while(result_agentcpe = wm_vuldet_step(stmt_agentcpe), result_agentcpe != SQLITE_DONE) {
                                switch (result_agentcpe) {
                                case SQLITE_ROW:
                                    rp->vulnerable = 1;
                                    wm_vuldet_add_extra_package(rp, vendor, product);
                                    break;

                                default:
                                    return wm_vuldet_sql_error(db, stmt_agentcpe);
                                }
                            }
                            wdb_finalize(stmt_agentcpe);
                            break;

                        default:
                            return wm_vuldet_sql_error(db, stmt_cpe);
                        }
                    }
                    wdb_finalize(stmt_cpe);
                    break;

                default:
                    return wm_vuldet_sql_error(db, stmt_match);
                }
            }
            wdb_finalize(stmt_match);
            break;

        default:
            return wm_vuldet_sql_error(db, stmt_conf);
        }
    }

    wdb_finalize(stmt_conf);
    return 0;
}

int wm_vuldet_add_extra_package(vu_nvd_report *rp, char *vendor, char *product) {
    unsigned int size = 0;

    if (rp->and_package) {
        size = strlen(vendor) + strlen(product) + 4;
        os_realloc(rp->and_package, size + strlen(rp->and_package) * sizeof(char), rp->and_package);
        char *pck_cnd;
        os_strdup(rp->and_package, pck_cnd);
        snprintf(rp->and_package, size + strlen(pck_cnd), "%s %s:%s ", pck_cnd, vendor, product);
        free(pck_cnd);
    } else {
        size = strlen(vendor) + strlen(product) + strlen("And package/s: : ");
        os_calloc(size, sizeof(char), rp->and_package);
        snprintf(rp->and_package, size, "And package/s: %s:%s ", vendor, product);
    }

    return 0;
}

int wm_vuldet_generate_agent_cpes_with_dic(sqlite3 *db, agent_software *agent) {
    cpe_list *node_list = NULL;
    int min_cpe_index;
    int retval = OS_INVALID;

    if (wm_vuldet_get_min_cpe_index(db, &min_cpe_index)) {
        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_CPE_INDEX_GET_ERROR);
        return OS_INVALID;
    }

    // Check for replace_vendor
    if (wm_vuldet_dic_check_rep_vendor(db, agent, &min_cpe_index, &node_list)) {
        goto end;
    }

    if (node_list && wm_vuldet_insert_cpe_db(db, node_list, 0)) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_WAZUH_DB_CPE_IN_ERROR, atoi(agent->agent_id));
    }

    if (wm_vuldet_update_agent_cpes(agent, db)) {
        mtdebug1(WM_VULNDETECTOR_LOGTAG, VU_AGENT_CPE_UPD_ERROR, atoi(agent->agent_id));
        goto end;
    }

    retval = 0;
end:
    wm_vuldet_free_cpe_list(node_list);
    free(node_list);
    return retval;
}

int wm_vuldet_generate_agent_cpes(sqlite3 *db, agent_software *agent, char dic) {
    if (dic) {
        return wm_vuldet_generate_agent_cpes_with_dic(db, agent);
    } else {
        return wm_vuldet_generate_agent_cpes_with_terms(db, agent);
    }
}

int wm_vuldet_get_dic_matches(sqlite3 *db, char *agent_id, sqlite3_stmt **stmt) {
    wdb_finalize(*stmt);

    if (wm_vuldet_prepare(db, vu_queries[VU_GET_DIC_MATCHES], -1, stmt, NULL) != SQLITE_OK) {
        goto error;
    }

    sqlite3_bind_text(*stmt, 1, agent_id, -1, NULL);

    return 0;
error:
    wm_vuldet_sql_error(db, *stmt);
    *stmt = NULL;
    return OS_INVALID;
}


int wm_vuldet_add_dic_cpe(sqlite3 *db, int *cpe_index, cpe_list **node_list, cpe *o_app, cpe *g_app) {
    sqlite3_stmt *stmt = NULL;
    int result;
    cpe *cpe_it;
    int pos;

    if (wm_vuldet_prepare(db, vu_queries[VU_UPDATE_AGENT_CPE], -1, &stmt, NULL) != SQLITE_OK) {
        wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_int(stmt, 1, *cpe_index);
    sqlite3_bind_int(stmt, 2, o_app->id);
    sqlite3_bind_text(stmt, 3, o_app->vendor, -1, NULL);
    sqlite3_bind_text(stmt, 4, o_app->product, -1, NULL);
    sqlite3_bind_text(stmt, 5, o_app->version, -1, NULL);
    sqlite3_bind_text(stmt, 6, o_app->target_hw, -1, NULL);

    if (result = wm_vuldet_step(stmt), result != SQLITE_DONE && result != SQLITE_CONSTRAINT) {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);


    if (!*node_list) {
        os_calloc(1, sizeof(cpe_list), *node_list);
    }

    for (cpe_it = g_app, pos = 0; cpe_it; cpe_it = cpe_it->next, pos++) {
        cpe_it->pos = pos;
        if (wm_vuldet_add_cpe(cpe_it, NULL, *node_list, *cpe_index)) {
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_AGENT_CPE_PARSE_ERROR, cpe_it->raw, o_app->id);
        } else {
            mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_AGENT_CPE_RECV, cpe_it->raw, o_app->id);
        }
    }

    *cpe_index = *cpe_index - 1;

    return 0;
}

int wm_vuldet_get_translate_terms(sqlite3 *db, int cpeh_id, int action, int vendor_pos, int product_pos, int version_pos, int sw_ed_pos, int msu_pos, cpe *g_app) {

    if (wm_vuldet_get_translate_term(db, cpeh_id, CPE_VENDOR, vendor_pos, g_app)) {
        return OS_INVALID;
    }

    if (wm_vuldet_get_translate_term(db, cpeh_id, CPE_PRODUCT, product_pos, g_app)) {
        return OS_INVALID;
    }

    if (action & CPE_DIC_SET_VER_IF_MATCH) {
        if (wm_vuldet_get_translate_term(db, cpeh_id, CPE_VERSION, version_pos, g_app)) {
            return OS_INVALID;
        }
    }

    if (action & CPE_DIC_REP_SWED_IF_PROD_MATCHES) {
        if (wm_vuldet_get_translate_term(db, cpeh_id, CPE_SW_EDITION, sw_ed_pos, g_app)) {
            return OS_INVALID;
        }
    }

    if (action & (CPE_DIC_REP_MSUN_IF_VER_MATCHES | CPE_DIC_REP_MSUN)) {
        if (wm_vuldet_get_translate_term(db, cpeh_id, CPE_MSU_NAME, msu_pos, g_app)) {
            return OS_INVALID;
        }
    }

    if (action & CPE_DIC_CHECK_HOTFIX) {
        g_app->check_hotfix = 1;
    }

    os_strdup("a", g_app->part);

    wm_vuldet_expand_msu_variables(g_app);
    wm_vuldet_solve_cond_translate_term(g_app);
    g_app->raw = wm_vuldet_cpe_str(g_app);

    return 0;
}

int wm_vuldet_get_correlation_id(sqlite3 *db, int cpeh_id, const char *type, char *search_term, char **match) {
    sqlite3_stmt *stmt = NULL;
    int retval = -1;

    if (wm_vuldet_prepare(db, vu_queries[VU_GET_EXACT_TERM], -1, &stmt, NULL) != SQLITE_OK) {
        wm_vuldet_sql_error(db, NULL);
        goto end;
    }
    sqlite3_bind_int(stmt, 1, cpeh_id);
    sqlite3_bind_text(stmt, 2, type, -1, NULL);
    if (!search_term) {
        sqlite3_bind_text(stmt, 3, "", -1, NULL);
        sqlite3_bind_text(stmt, 4, "", -1, NULL);
    } else {
        sqlite3_bind_text(stmt, 3, search_term, -1, NULL);
        sqlite3_bind_text(stmt, 4, search_term, -1, NULL);
    }

    switch (wm_vuldet_step(stmt)) {
        case SQLITE_ROW:
            // The combination exists
            if (match) {
                w_strdup((char *) sqlite3_column_text(stmt, 0), *match);
            }
            retval = sqlite3_column_int(stmt, 1);
        break;
        case SQLITE_DONE:
            retval = -2;
            goto end;
        break;
        default:
            wm_vuldet_sql_error(db, NULL);
            goto end;
    }

end:
    wdb_finalize(stmt);
    return retval;
}

int wm_vuldet_dic_check_rep_vendor(sqlite3 *db, agent_software *agent, int *min_cpe_index, cpe_list **node_list) {
    int retval = OS_INVALID;
    int result;
    cpe o_app;
    cpe *g_app = NULL;
    sqlite3_stmt *stmt_vendor = NULL;
    sqlite3_stmt *stmt_product = NULL;
    sqlite3_stmt *stmt_version = NULL;
    sqlite3_stmt *stmt_translation = NULL;

    // Create the Regex callback
    sqlite3_create_function_v2(db, "REGEXP", 2, SQLITE_ANY, 0, &w_sql_regex, 0, 0, 0);

    if (wm_vuldet_get_dic_matches(db, agent->agent_id, &stmt_vendor)) {
        goto end;
    } else {
        while (result = wm_vuldet_step(stmt_vendor), result == SQLITE_ROW) {
            int correlation_id;
            int ven_correlation_id = 0;
            int pr_correlation_id = 0;
            int ver_correlation_id = 0;
            int sw_ed_correlation_id = 0;
            int msu_correlation_id = 0;
            char *vendor = (char *) sqlite3_column_text(stmt_vendor, 0);
            char *product = (char *) sqlite3_column_text(stmt_vendor, 1);
            char *version = (char *) sqlite3_column_text(stmt_vendor, 2);
            char *arch = (char *) sqlite3_column_text(stmt_vendor, 3);
            int cpeh_id = sqlite3_column_int(stmt_vendor, 4);
            unsigned int cpeh_action = (unsigned int) sqlite3_column_int(stmt_vendor, 5);

            if (cpeh_action & CPE_DIC_IGNORE) {
                // Skip
                continue;
            }

            if (g_app) {
                wm_vuldet_free_multi_cpe(g_app);
            }
            os_calloc(1, sizeof(cpe), g_app);

            o_app.id = atoi(agent->agent_id);
            o_app.vendor = vendor;
            o_app.product = product;
            o_app.version = version;
            o_app.target_hw = arch;

            // If replace_vendor_if_matches
            if (cpeh_action & CPE_DIC_REP_VEN_IF_MATCH) {
                if (ven_correlation_id = wm_vuldet_get_correlation_id(db, cpeh_id, vu_cpe_tags[CPE_VENDOR], vendor, NULL), ven_correlation_id < 0) {
                    if (ven_correlation_id == -2) {
                        continue;
                    }
                    goto end;
                }
            }

            // Check replace_vendor and replace_product/replace_product_if_matches combinations
            if (cpeh_action & (CPE_DIC_REP_PROD_IF_MATCH | CPE_DIC_REP_PROD)) {

                // If replace_product_if_match or replace_sw_edition_if_product_match
                if (cpeh_action & (CPE_DIC_REP_PROD_IF_MATCH | CPE_DIC_REP_SWED_IF_PROD_MATCHES)) {
                    if (correlation_id = wm_vuldet_get_correlation_id(db, cpeh_id, vu_cpe_tags[CPE_PRODUCT], product, NULL), correlation_id < 0) {
                        if (correlation_id == -2) {
                            continue;
                        }
                        goto end;
                    }

                    if (cpeh_action & CPE_DIC_REP_SWED_IF_PROD_MATCHES) {
                        sw_ed_correlation_id = correlation_id;

                    }

                    if (cpeh_action & CPE_DIC_REP_PROD_IF_MATCH) {
                        pr_correlation_id = correlation_id;
                    }
                }

                // If set_version_if_matches or replace_msu_name_if_version_matches
                if (cpeh_action & (CPE_DIC_SET_VER_IF_MATCH | CPE_DIC_REP_MSUN_IF_VER_MATCHES)) {
                    if (correlation_id = wm_vuldet_get_correlation_id(db, cpeh_id, vu_cpe_tags[CPE_VERSION], version, NULL), correlation_id < 0) {
                        if (correlation_id == -2) {
                            continue;
                        }
                        goto end;
                    }

                    if (cpeh_action & CPE_DIC_SET_VER_IF_MATCH) {
                        ver_correlation_id = correlation_id;
                    }

                    if (cpeh_action & CPE_DIC_REP_MSUN_IF_VER_MATCHES && cpeh_action & CPE_DIC_CHECK_HOTFIX) {
                        msu_correlation_id = correlation_id;
                    }
                }

                // If set_version_only_if_product_matches or set_version_if_product_matches
                if (cpeh_action & (CPE_DIC_SET_VER_IF_PROD_MATCHES | CPE_DIC_SET_VER_ONLY_IF_PROD_MATCHES)) {
                    char *pattern = NULL;
                    if (correlation_id = wm_vuldet_get_correlation_id(db, cpeh_id, vu_cpe_tags[CPE_VERSION], product, &pattern), correlation_id < 0) {
                        free(pattern);
                        if (correlation_id == -2) {
                            continue;
                        }
                        goto end;
                    }

                    if (g_app->version = wm_vuldet_extract_product_field(pattern, product), !g_app->version && cpeh_action & CPE_DIC_SET_VER_ONLY_IF_PROD_MATCHES) {
                        continue;
                    }

                    free(pattern);
                }

                // If set_targethw_only_if_product_matches or set_targethw_if_product_matches
                if (cpeh_action & (CPE_DIC_SET_TARGETHW_IF_PRODUCT_MATCHES | CPE_DIC_SET_TARGETHW_ONLY_IF_PRODUCT_MATCHES)) {
                    char *pattern = NULL;
                    if (correlation_id = wm_vuldet_get_correlation_id(db, cpeh_id, vu_cpe_tags[CPE_TARGET_HW], product, &pattern), correlation_id < 0) {
                        free(pattern);
                        if (correlation_id == -2) {
                            continue;
                        }
                        goto end;
                    }

                    if (g_app->target_hw = wm_vuldet_extract_product_field(pattern, product), !g_app->target_hw && cpeh_action & CPE_DIC_SET_TARGETHW_ONLY_IF_PRODUCT_MATCHES) {
                        continue;
                    }

                    wm_vuldet_adapt_targethw(&g_app->target_hw);
                    free(pattern);
                }

                if (!g_app->version) {
                    w_strdup(version, g_app->version);
                }

                if (arch && strcmp(arch, "unknown")) {
                    free(g_app->target_hw);
                    w_strdup(arch, g_app->target_hw);
                }


                // Get the translation terms
                if (wm_vuldet_get_translate_terms(db, cpeh_id, cpeh_action, ven_correlation_id, pr_correlation_id, ver_correlation_id, sw_ed_correlation_id, msu_correlation_id, g_app)) {
                    goto end;
                }

                if (wm_vuldet_add_dic_cpe(db, min_cpe_index, node_list, &o_app, g_app)) {
                    goto end;
                }
            }
        }

        if (result != SQLITE_DONE) {
            wm_vuldet_sql_error(db, NULL);
            goto end;
        }
    }

    retval = 0;
end:
    if (g_app) {
        wm_vuldet_free_multi_cpe(g_app);
    }

    wdb_finalize(stmt_vendor);
    wdb_finalize(stmt_product);
    wdb_finalize(stmt_version);
    wdb_finalize(stmt_translation);

    return retval;
}

int wm_vuldet_get_translate_term(sqlite3 *db, int cpeh_id, cpe_tags type, int pos, cpe *g_app) {
    sqlite3_stmt *stmt;
    int result;
    int i = 0;
    char **term = NULL;
    translation_cond **m_term = NULL;

    switch (type) {
        case CPE_VENDOR:
            term = &g_app->vendor;
        break;
        case CPE_PRODUCT:
            term = &g_app->product;
            m_term = &g_app->cm_product;
        break;
        case CPE_VERSION:
            term = &g_app->version;
        break;
        case CPE_SW_EDITION:
            term = &g_app->sw_edition;
        break;
        case CPE_MSU_NAME:
            term = &g_app->msu_name;
        break;
        default:
            return OS_INVALID;
    }

    if (wm_vuldet_prepare(db, vu_queries[VU_GET_TRANSLATION_TERM], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_int(stmt, 1, cpeh_id);
    sqlite3_bind_text(stmt, 2, vu_cpe_tags[type], -1, NULL);
    sqlite3_bind_int(stmt, 3, pos);

    while (result = wm_vuldet_step(stmt), result == SQLITE_ROW) {
        const char *r_term = (const char *) sqlite3_column_text(stmt, 0);
        const char *r_op = (const char *) sqlite3_column_text(stmt, 1);
        const char *r_cond = (const char *) sqlite3_column_text(stmt, 2);

        if (!i && !r_op) {
            os_strdup(r_term, *term);
        } else if (m_term) {
            os_realloc(*m_term, (i + 2) * sizeof(translation_cond), *m_term);
            memset((void *) &(*m_term)[i], 0, 2 * sizeof(translation_cond));

            if (*term) {
                (*m_term)[0].translation = *term;
                (*m_term)[0].field = NULL;
                (*m_term)[0].condition = NULL;
                *term = NULL;
            }

            os_strdup(r_term, (*m_term)[i].translation);
            os_strdup(r_op, (*m_term)[i].field);
            os_strdup(r_cond, (*m_term)[i].condition);
        } else {
            mterror(WM_VULNDETECTOR_LOGTAG, VU_INVALID_TERM_CONDITION, vu_cpe_tags[type], cpeh_id, pos);
            wdb_finalize(stmt);
            return OS_INVALID;
        }
        i++;
    }

    if (result != SQLITE_DONE) {
        return wm_vuldet_sql_error(db, stmt);
    }

    wdb_finalize(stmt);
    return 0;
}

vu_logic wm_vuldet_check_hotfix(sqlite3 *db, agent_software *agent, wm_vuldet_flags *flags, int cve_id, cpe *s_cpe, char **necessary_patch) {
    sqlite3_stmt *stmt = NULL;
    sqlite3_stmt *htfx_stmt = NULL;
    sqlite3_stmt *msu_stmt = NULL;
    const char *cve;
    vu_logic retval = VU_NOT_VULNERABLE;
    char product_name[OS_SIZE_128 + 1];
    char patch_find[50 + 1];
    char *request = NULL;
    int result;

    // Get the vulnerability string code
    if (wm_vuldet_prepare(db, vu_queries[VU_GET_CVE], -1, &stmt, NULL) != SQLITE_OK) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_SQL_ERROR, sqlite3_errmsg(db));
        goto end;
    }
    sqlite3_bind_int(stmt, 1, cve_id);

    if (wm_vuldet_step(stmt) == SQLITE_ROW) {
        cve = (char *)sqlite3_column_text(stmt, 0);
    } else {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_SQL_ERROR, sqlite3_errmsg(db));
        goto end;
    }

    // Get the related patches
    request = wm_vuldet_generate_msu_request(s_cpe,
                                            agent->dist_ver);

    snprintf(product_name, OS_SIZE_128, "^%s.*", s_cpe->msu_name);

    if (wm_vuldet_prepare(db, request, -1, &msu_stmt, NULL) != SQLITE_OK) {
        mterror(WM_VULNDETECTOR_LOGTAG, VU_SQL_ERROR, sqlite3_errmsg(db));
        goto end;
    }
    sqlite3_bind_text(msu_stmt, 1, cve, -1, NULL);
    sqlite3_bind_text(msu_stmt, 2, product_name, -1, NULL);

    if (wm_vuldet_step(msu_stmt) == SQLITE_ROW) {
        do {
            const char *patch = (char *)sqlite3_column_text(msu_stmt, 0);
            const char *super = (char *)sqlite3_column_text(msu_stmt, 1);

            if (!patch || !super) {
                continue;
            }
            snprintf(patch_find, 50, "%%%s", super);
            // Check the installed hotfix
            if (wm_vuldet_prepare(db, vu_queries[VU_CHECK_AGENT_HOTFIX], -1, &htfx_stmt, NULL) != SQLITE_OK) {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_SQL_ERROR, sqlite3_errmsg(db));
                goto end;
            }

            sqlite3_bind_text(htfx_stmt, 1, agent->agent_id, -1, NULL);
            sqlite3_bind_text(htfx_stmt, 2, patch_find, -1, NULL);

            if (result = wm_vuldet_step(htfx_stmt), result == SQLITE_ROW) {
                patch = (char *)sqlite3_column_text(htfx_stmt, 0);
                mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_HOTFIX_INSTALLED, atoi(agent->agent_id), patch, cve);
                retval = VU_NOT_VULNERABLE;
                os_free(*necessary_patch);
                goto end;
            } else if (result == SQLITE_DONE) {
                if (!*necessary_patch) {
                    w_strdup(patch, *necessary_patch);
                }
                retval = VU_VULNERABLE;
            } else {
                mterror(WM_VULNDETECTOR_LOGTAG, VU_SQL_ERROR, sqlite3_errmsg(db));
                goto end;
            }
            wdb_finalize(htfx_stmt);
        } while (wm_vuldet_step(msu_stmt) == SQLITE_ROW);
    } else if (flags->permissive_patch_scan) {
        retval = VU_VULNERABLE;
        os_strdup("|", *necessary_patch);
    } else {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_NO_HOTFIX_FEED, cve, atoi(agent->agent_id));
    }

end:
    free(request);
    wdb_finalize(msu_stmt);
    wdb_finalize(stmt);
    wdb_finalize(htfx_stmt);
    return retval;
}

int wm_vuldet_index_nvd_metadata(sqlite3 *db, int year, int cve_count, char alternative) {
    FILE *fp = NULL;
    int retval = OS_INVALID;
    char buffer[OS_MAXSTR + 1];
    char *last_mod = NULL;
    char *size = NULL;
    char *zip_size = NULL;
    char *g_size = NULL;
    char *sha256 = NULL;
    static char *feed_last_mod = "lastModifiedDate:";
    static char *feed_size = "size:";
    static char *feed_zsize = "zipSize:";
    static char *feed_gsize = "gzSize:";
    static char *feed_sha256 = "sha256:";

    if (!alternative) {
        if (fp = fopen(VU_TEMP_METADATA_FILE, "r"), !fp) {
            sqlite3_close_v2(db);
            goto end;
        }

        while (fgets(buffer, OS_MAXSTR, fp)) {
            char *found;
            size_t bsize = strlen(buffer);
            if (buffer[bsize - 1] == '\n' || buffer[bsize - 1] == '\r') {
                buffer[bsize - 1] = '\0';
            }
            if (buffer[bsize - 2] == '\n' || buffer[bsize - 2] == '\r') {
                buffer[bsize - 2] = '\0';
            }

            if (found = strstr(buffer, feed_last_mod), found) {
                if (!last_mod) os_strdup(found + strlen(feed_last_mod), last_mod);
            } else if (found = strstr(buffer, feed_size), found) {
                if (!size) os_strdup(found + strlen(feed_size), size);
            } else if (found = strstr(buffer, feed_zsize), found) {
                if (!zip_size) os_strdup(found + strlen(feed_zsize), zip_size);
            } else if (found = strstr(buffer, feed_gsize), found) {
                if (!g_size) os_strdup(found + strlen(feed_gsize), g_size);
            } else if (found = strstr(buffer, feed_sha256), found) {
                if (!sha256) os_strdup(found + strlen(feed_sha256), sha256);
            }
        }
    } else {
        time_t now = time(NULL);
        struct tm * time_info = localtime(&now);
        os_calloc(100 + 1, sizeof(char), last_mod);
        strftime(last_mod, 100, "%Y/%m/%dT%H:%M:%S", time_info);
    }

    // Update the metadata entry
    if (wm_vuldet_insert_nvd_metadata(db, year, size, zip_size, g_size, sha256, last_mod, cve_count, alternative)) {
        goto end;
    }

    retval = 0;
end:
    free(size);
    free(zip_size);
    free(g_size);
    free(sha256);
    free(last_mod);

    w_fclose(fp);
    if (remove(VU_TEMP_METADATA_FILE) < 0 && errno != ENOENT) {
        mtdebug2(WM_VULNDETECTOR_LOGTAG, "remove(%s): %s", VU_TEMP_METADATA_FILE, strerror(errno));
    }
    return retval;
}

int wm_vuldet_clean_nvd(sqlite3 *db) {
    char open_db = 0;

    if (!db) {
        if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, NULL);
        }

        open_db = 1;
    }

    if (wm_vuldet_remove_table(db, "NVD_METADATA") ||
        wm_vuldet_remove_table(db, "NVD_CVE") ||
        wm_vuldet_remove_table(db, "NVD_METRIC_CVSS") ||
        wm_vuldet_remove_table(db, "NVD_REFERENCE") ||
        wm_vuldet_remove_table(db, "NVD_CVE_CONFIGURATION") ||
        wm_vuldet_remove_table(db, "NVD_CVE_MATCH") ||
        wm_vuldet_remove_table(db, "NVD_CPE")) {
        return OS_INVALID;
    }
    if (open_db) {
        sqlite3_close_v2(db);
    }
    return 0;
}

int wm_vuldet_has_offline_update(sqlite3 *db) {
    sqlite3_stmt *stmt = NULL;
    int retval = 0;

    if (wm_vuldet_prepare(db, vu_queries[VU_GET_OFFLINE_UPDATE], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }
    if (wm_vuldet_step(stmt) == SQLITE_ROW) {
        retval = 1;
    }

    wdb_finalize(stmt);
    return retval;
}

int wm_vuldet_clean_nvd_year(sqlite3 *db, int year) {
    char open_db = 0;
    sqlite3_stmt *stmt = NULL;
    sqlite3_stmt *stmt2 = NULL;
    char year_str[11];

    snprintf(year_str, 10, "%d", year);

    if (!db) {
        if (sqlite3_open_v2(CVE_DB, &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
            return wm_vuldet_sql_error(db, NULL);
        }

        open_db = 1;
    }

    // Clean metadata
    if (wm_vuldet_clean_nvd_metadata(db, year)) {
        return OS_INVALID;
    }

    if (wm_vuldet_prepare(db, vu_queries[VU_GET_NVD_CVE_YEAR], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_int(stmt, 1, year);

    while (wm_vuldet_step(stmt) == SQLITE_ROW) {
        int id = sqlite3_column_int(stmt, 0);
        const char *sql;
        const char *tail;

        // Clean matches
        if (wm_vuldet_prepare(db, vu_queries[VU_GET_NVD_CONFIG], -1, &stmt2, NULL) != SQLITE_OK) {
            wdb_finalize(stmt);
            return wm_vuldet_sql_error(db, stmt2);
        }

        sqlite3_bind_int(stmt2, 1, id);

        while (wm_vuldet_step(stmt2) == SQLITE_ROW) {
            sqlite3_stmt *stmt3 = NULL;
            int id_config = sqlite3_column_int(stmt2, 0);

            if (wm_vuldet_prepare(db, vu_queries[VU_REMOVE_CVE_MATCHES], -1, &stmt3, NULL) != SQLITE_OK) {
                wdb_finalize(stmt);
                wdb_finalize(stmt2);
                return wm_vuldet_sql_error(db, stmt3);
            }

            sqlite3_bind_int(stmt3, 1, id_config);

            if (wm_vuldet_step(stmt3) != SQLITE_DONE) {
                wdb_finalize(stmt);
                wdb_finalize(stmt2);
                return wm_vuldet_sql_error(db, stmt3);
            }
            wdb_finalize(stmt3);
        }
        wdb_finalize(stmt2);

        // Clean configuration, references and metrics
        for (sql = vu_queries[VU_REMOVE_CVE_TABLE_REFS]; sql && *sql; sql = tail) {
            if (wm_vuldet_prepare(db, sql, -1, &stmt2, &tail) != SQLITE_OK) {
                wdb_finalize(stmt);
                return wm_vuldet_sql_error(db, stmt2);
            }
            sqlite3_bind_int(stmt2, 1, id);

            if (wm_vuldet_step(stmt2) != SQLITE_DONE) {
                wdb_finalize(stmt);
                return wm_vuldet_sql_error(db, stmt2);
            }
            wdb_finalize(stmt2);
        }
    }
    wdb_finalize(stmt);

    if (wm_vuldet_prepare(db, vu_queries[VU_REMOVE_NVD_CVE], -1, &stmt, NULL) != SQLITE_OK) {
        return wm_vuldet_sql_error(db, stmt);
    }

    sqlite3_bind_text(stmt, 1, year_str, -1, NULL);

    if (wm_vuldet_step(stmt) != SQLITE_DONE) {
        return wm_vuldet_sql_error(db, stmt);
    }
    wdb_finalize(stmt);

    sqlite3_exec(db, vu_queries[VU_REMOVE_NVD_CPE], NULL, NULL, NULL);

    if (open_db) {
        sqlite3_close_v2(db);
    }
    return 0;
}

void wm_vuldet_expand_msu_variables(cpe *g_app) {
    char *replaced;
    static const char *VAR_VERSION = "$(VERSION)";
    static const char *VAR_ARCH = "$(ARCHITECTURE)";

    if (g_app->msu_name) {
        if (g_app->version && strstr(g_app->msu_name, VAR_VERSION)) {
            replaced = wstr_replace(g_app->msu_name, VAR_VERSION, g_app->version);
            free(g_app->msu_name);
            g_app->msu_name = replaced;
        }

        if (g_app->target_hw && strstr(g_app->msu_name, VAR_ARCH)) {
            replaced = wstr_replace(g_app->msu_name, VAR_ARCH, g_app->target_hw);
            free(g_app->msu_name);
            g_app->msu_name = replaced;
        }
    }
}

char *wm_vuldet_extract_product_field(char *pattern, char *product) {
    regex_t regex;
    regmatch_t *matches;
    char *retval = NULL;

    os_calloc(2, sizeof(regmatch_t), matches);

    if (regcomp(&regex, pattern, REG_EXTENDED)) {
        goto end;
    }

    if (!regexec(&regex, product, 2, matches, 0)) {
        int start = (&matches[1])->rm_so;
        int end = (&matches[1])->rm_eo;

        if (start >= 0 && end >= 0 && end > start && (unsigned int) (end - start) <= strlen(product)) {
            os_calloc(end - start + 1, sizeof(char), retval);
            strncpy(retval, product + start, end - start);
        }
    }

    regfree(&regex);
end:
    free(matches);
    return retval;
}

void wm_vuldet_solve_cond_translate_term(cpe *g_app) {
    int i;
    cpe *last_cpe = NULL;

    if (g_app->cm_product) {
        for (i = 0; g_app->cm_product[i].translation; i++) {
            if (wm_vuldet_eval_cond_term(g_app, &g_app->cm_product[i])) {
                if (!g_app->product) {
                    os_strdup(g_app->cm_product[i].translation, g_app->product);
                } else {
                    if (!last_cpe) {
                        g_app->next = wm_vuldet_cpe_cpy(g_app);
                        last_cpe = g_app->next;
                    } else {
                        last_cpe->next = wm_vuldet_cpe_cpy(g_app);
                        last_cpe = last_cpe->next;
                    }
                    free(last_cpe->product);
                    os_strdup(g_app->cm_product[i].translation, last_cpe->product);
                    free(last_cpe->raw);
                    last_cpe->raw = wm_vuldet_cpe_str(last_cpe);
                }
            }
        }

        if (!g_app->product) {
            os_strdup("", g_app->product);
        }
    }
}

int wm_vuldet_eval_cond_term(cpe *g_app, translation_cond *cond) {
    vu_ver_comp op;
    const char *condition = cond->condition;

    if (!cond->field || !condition) {
        return 1;
    }

    if (!strncmp(condition, "<", 2)) {
        op = VU_COMP_L;
        condition += 2;
    } else if (!strncmp(condition, "<=", 2)) {
        op = VU_COMP_LE;
        condition += 3;
    } else if (!strncmp(condition, ">", 2)) {
        op = VU_COMP_G;
        condition += 2;
    } else if (!strncmp(condition, ">=", 2)) {
        op = VU_COMP_GE;
        condition += 3;
    } else if (!strncmp(condition, "==", 2)) {
        op = VU_COMP_EQ;
        condition += 3;
    } else if (!strncmp(condition, "!=", 2)) {
        op = VU_COMP_NEQ;
        condition += 3;
    } else {
        return 0;
    }

    if (!strcmp(cond->field, "version")) {
        if (wm_checks_package_vulnerability(g_app->version, vu_package_comp[op], condition, VER_TYPE_NVD) == VU_VULNERABLE) {
            return 1;
        }
    }

    return 0;
}

cpe *wm_vuldet_cpe_cpy(cpe *orig) {
    cpe *cpy;

    os_calloc(1, sizeof(cpe), cpy);
    w_strdup(orig->part, cpy->part);
    w_strdup(orig->vendor, cpy->vendor);
    w_strdup(orig->product, cpy->product);
    w_strdup(orig->version, cpy->version);
    w_strdup(orig->update, cpy->update);
    w_strdup(orig->edition, cpy->edition);
    w_strdup(orig->language, cpy->language);
    w_strdup(orig->sw_edition, cpy->sw_edition);
    w_strdup(orig->target_sw, cpy->target_sw);
    w_strdup(orig->target_hw, cpy->target_hw);
    w_strdup(orig->other, cpy->other);
    cpy->id = orig->id;
    cpy->pos = orig->pos;
    w_strdup(orig->msu_name, cpy->msu_name);
    cpy->check_hotfix = orig->check_hotfix;
    w_strdup(orig->raw, cpy->raw);

    return cpy;
}

void wm_vuldet_adapt_targethw(char **target_hw) {
    if (*target_hw) {
        if (!strcmp("32-bit", *target_hw)) {
            os_free(*target_hw);
            os_strdup("i686", *target_hw);
        } else if (!strcmp("64-bit", *target_hw)) {
            os_free(*target_hw);
            os_strdup("x86_64", *target_hw);
        }
    } else {
        os_strdup("unknown", *target_hw);
    }
}

void wm_vuldet_fit_nvd_matches(const char *cve, nvd_conf_cpe_match **match_list) {
    if (*match_list) {
        nvd_conf_cpe_match *cpe_match_node = *match_list;
        nvd_conf_cpe_match *previous_node = NULL;
        while (cpe_match_node) {
            char removed = 0;

            // Check if it is not a range or glob
            if (!cpe_match_node->cpe_node ||
                !cpe_match_node->cpe_node->version ||
                !!cpe_match_node->cpe_node->part ||
                // We just want to index the generic versions of the operating systems
                (strcmp(cpe_match_node->cpe_node->version, "o") &&
                !strcmp(cpe_match_node->cpe_node->version, "*")) ||
                cpe_match_node->version_start_including ||
                cpe_match_node->version_start_excluding ||
                cpe_match_node->version_end_including ||
                cpe_match_node->version_end_excluding) {
                    goto next_it;
            }

            nvd_conf_cpe_match *match_it;
            for (match_it = cpe_match_node->next; match_it; match_it = match_it->next) {
                char *si = match_it->version_start_including;
                char *se = match_it->version_start_excluding;
                char *ei = match_it->version_end_including;
                char *ee = match_it->version_end_excluding;

                if (!si && !se && !ei && !ee) {
                    continue;
                }

                vu_logic s_inc = si ? wm_checks_package_vulnerability(cpe_match_node->cpe_node->version, vu_package_comp[VU_COMP_GE], si, VER_TYPE_NVD)
                                    : VU_UNDEFINED;
                vu_logic s_exc = se ? wm_checks_package_vulnerability(cpe_match_node->cpe_node->version, vu_package_comp[VU_COMP_G], se, VER_TYPE_NVD)
                                    : VU_UNDEFINED;
                vu_logic e_inc = ei ? wm_checks_package_vulnerability(cpe_match_node->cpe_node->version, vu_package_comp[VU_COMP_LE], ei, VER_TYPE_NVD)
                                    : VU_UNDEFINED;
                vu_logic e_exc = ee ? wm_checks_package_vulnerability(cpe_match_node->cpe_node->version, vu_package_comp[VU_COMP_L], ee, VER_TYPE_NVD)
                                    : VU_UNDEFINED;

                if ((s_inc == VU_UNDEFINED || s_inc == VU_VULNERABLE) &&
                    (s_exc == VU_UNDEFINED || s_exc == VU_VULNERABLE) &&
                    (e_inc == VU_UNDEFINED || e_inc == VU_VULNERABLE) &&
                    (e_exc == VU_UNDEFINED || e_exc == VU_VULNERABLE)) {
                        mtdebug2(WM_VULNDETECTOR_LOGTAG, VU_DISCARD_MATCH_N,
                                    cpe_match_node->cpe_uri23,
                                    cve ? cve : "<unknown CVE>",
                                    match_it->cpe_uri23,
                                    si ? vu_package_comp[VU_COMP_GE] : se ? vu_package_comp[VU_COMP_G] : "",
                                    si || se ? " " : "",
                                    si ? si : se ? se : "",
                                    (si || se) && (ei || ee) ? " and " : "",
                                    ei ? vu_package_comp[VU_COMP_LE] : ee ? vu_package_comp[VU_COMP_L] : "",
                                    ee || ee ? " " : "",
                                    ei ? ei : ee ? ee : ""
                                );

                    nvd_conf_cpe_match *r_node = cpe_match_node;
                    cpe_match_node = cpe_match_node->next;
                    if (previous_node) {
                        previous_node->next = cpe_match_node;
                    } else {
                        *match_list = cpe_match_node;
                    }
                    wm_vuldet_free_nvd_conf_cpe_match_node(r_node);

                    removed = 1;
                    break;
                }
            }

next_it:
            if (!removed) {
                previous_node = cpe_match_node;
                cpe_match_node = cpe_match_node->next;
            }
        }
    }
}

void wm_vuldet_free_cpe_list(cpe_list *node_list) {
    cpe_node *r_node;
    cpe_node *node_it = node_list ? node_list->first : NULL;

    while (node_it) {
        wm_vuldet_free_cpe(node_it->node);
        r_node = node_it->next;
        os_free(node_it);
        node_it = r_node;
    }
}

void wm_vuldet_adapt_os_cpe(cpe *ag_cpe) {
    if (*ag_cpe->part != 'o') {
        return;
    }

    if ( // Check if the service pack is in the version field
        (*ag_cpe->version == 's' && wm_vuldet_is_cpe_wc(ag_cpe->update) && (
        !strcmp(ag_cpe->version, "sp1") ||
        !strcmp(ag_cpe->version, "sp2") ||
        !strcmp(ag_cpe->version, "sp3") ||
        !strcmp(ag_cpe->version, "sp4") ||
        !strcmp(ag_cpe->version, "sp5"))
        ) ||
        (  // Check if the r2 value is in the update field
        !strcmp(ag_cpe->update, "r2") && wm_vuldet_is_cpe_wc(ag_cpe->version)
        )) {
        // Change the version field by update
        char *s_pack = ag_cpe->version;
        ag_cpe->version = ag_cpe->update;
        ag_cpe->update = s_pack;
    }
}

int wm_vuln_valid_os_cpe(vu_feed dist, cpe *ag_cpe, char *c_version) {
    if (ag_cpe-> part && *ag_cpe->part != 'o') {
        return 1;
    }

    if (dist == FEED_WS2008 || dist == FEED_WS2012) {
        // Verify that the version field is not R2
        if (!strcmp(c_version, "r2")) {
            // The cpe is for server 2008/2012 R2
            return 2;
        }
    } else if (dist == FEED_WS2008R2 || dist == FEED_WS2012R2) {
        // Verify that the version field is R2
        if (strcmp(c_version, "r2")) {
            // The cpe is for server 2008/2012 without R2
            return 2;
        }
    }

    return 0;
}

char *wm_vuldet_generate_msu_request(cpe *s_cpe, vu_feed dist_ver) {
    char *request;
    char extra_check[OS_SIZE_256] = { '\0' };
    const char *sp_check_template = " AND (M1.PRODUCT NOT LIKE '%%Service Pack%%' OR "\
                                    "M1.PRODUCT LIKE '%%Service Pack %c%%')";
    const char *w10_check_template = " AND M1.CHECK_TYPE IS NOT 0 AND "\
                                     " M1.CVEID IN (SELECT CVEID FROM MSU WHERE CVEID = M1.CVEID AND CHECK_TYPE IS %s) AND "\
                                     " M1.CHECK_TYPE <= %s";

    os_calloc(OS_SIZE_512, sizeof(char), request);

    if (*s_cpe->part != 'o') {
        snprintf(request, OS_SIZE_512, vu_queries[VU_HOTFX_SIMPLE], "");
        return request;
    }

    if (s_cpe->update && !wm_vuldet_is_cpe_wc(s_cpe->update)) {
        if (!strncmp(s_cpe->update, "sp", 2)) {
            snprintf(extra_check, OS_SIZE_256, sp_check_template, s_cpe->update[2]);
        }
    } else if (s_cpe->version && dist_ver == FEED_W10 && !wm_vuldet_is_cpe_wc(s_cpe->version)) {
        snprintf(extra_check, OS_SIZE_256, w10_check_template, s_cpe->version, s_cpe->version);
    }

    snprintf(request, OS_SIZE_512,
                        vu_queries[wm_vulndet_without_r2(dist_ver)
                            ? VU_HOTFX_WITHOUT_R2
                            : VU_HOTFX_SIMPLE],
                        *extra_check ? extra_check : "");

    return request;
}

#endif
